From 7d779d84617af567370f999a0129d7dea385a3e0 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 20 Jun 2026 18:37:44 +0200 Subject: [PATCH 01/60] Feature/add data gathering frequency to symbol profile (#7083) * Add data gathering frequency to symbol profile and gather hourly * Update changelog --- CHANGELOG.md | 2 + apps/api/src/app/admin/admin.service.ts | 2 + .../market-data/market-data.controller.ts | 6 +- apps/api/src/app/import/import.service.ts | 2 + apps/api/src/app/symbol/symbol.service.ts | 25 ++++-- apps/api/src/services/cron/cron.service.ts | 1 + .../market-data/market-data.service.ts | 13 +++ .../data-gathering/data-gathering.service.ts | 83 +++++++++++++++++-- .../symbol-profile/symbol-profile.service.ts | 2 + .../asset-profile-dialog.component.ts | 21 +++++ .../asset-profile-dialog.html | 17 +++- .../src/lib/dtos/update-asset-profile.dto.ts | 12 ++- .../enhanced-symbol-profile.interface.ts | 8 +- .../responses/export-response.interface.ts | 1 + libs/ui/src/lib/services/admin.service.ts | 2 + .../migration.sql | 8 ++ prisma/schema.prisma | 7 ++ 17 files changed, 196 insertions(+), 16 deletions(-) create mode 100644 prisma/migrations/20260620163851_added_data_gathering_frequency_to_symbol_profile/migration.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index d4afa84a9..038612a02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added an icon to indicate external links in the page tabs component - Added the Korean (`ko`) language to the footer +- Added a data gathering frequency (`DAILY` or `HOURLY`) to the asset profile to control the market data gathering interval ### Changed +- Changed the _Fear & Greed Index_ (market mood) in the markets overview to use the stored market data instead of a live quote - Moved the endpoint to get the asset profiles from `GET api/v1/admin/market-data` to `GET api/v1/asset-profiles` - Added the selected asset profile count to the delete menu item of the historical market data table in the admin control panel - Added the selected asset profile count to the deletion confirmation dialog of the historical market data table in the admin control panel diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index f08fb6a37..9a5429655 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -287,6 +287,7 @@ export class AdminService { comment, countries, currency, + dataGatheringFrequency, dataSource: newDataSource, holdings, isActive, @@ -370,6 +371,7 @@ export class AdminService { const updatedSymbolProfile: Prisma.SymbolProfileUpdateInput = { comment, currency, + dataGatheringFrequency, dataSource, isActive, scraperConfiguration, diff --git a/apps/api/src/app/endpoints/market-data/market-data.controller.ts b/apps/api/src/app/endpoints/market-data/market-data.controller.ts index f6857283b..f60eea904 100644 --- a/apps/api/src/app/endpoints/market-data/market-data.controller.ts +++ b/apps/api/src/app/endpoints/market-data/market-data.controller.ts @@ -64,14 +64,16 @@ export class MarketDataController { dataGatheringItem: { dataSource: ghostfolioFearAndGreedIndexDataSourceCryptocurrencies, symbol: ghostfolioFearAndGreedIndexSymbolCryptocurrencies - } + }, + useIntradayData: true }), this.symbolService.get({ includeHistoricalData, dataGatheringItem: { dataSource: ghostfolioFearAndGreedIndexDataSourceStocks, symbol: ghostfolioFearAndGreedIndexSymbolStocks - } + }, + useIntradayData: true }) ]); diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index b82f763a0..2ecc4d3a5 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -536,6 +536,8 @@ export class ImportService { url, comment: assetProfile.comment, currency: assetProfile.currency, + dataGatheringFrequency: + assetProfile.dataGatheringFrequency ?? 'DAILY', userId: dataSource === 'MANUAL' ? user.id : undefined }, symbolProfileId: undefined, diff --git a/apps/api/src/app/symbol/symbol.service.ts b/apps/api/src/app/symbol/symbol.service.ts index fdbc7f84c..f2bf4beb1 100644 --- a/apps/api/src/app/symbol/symbol.service.ts +++ b/apps/api/src/app/symbol/symbol.service.ts @@ -24,15 +24,30 @@ export class SymbolService { public async get({ dataGatheringItem, - includeHistoricalData + includeHistoricalData, + useIntradayData = false }: { dataGatheringItem: DataGatheringItem; includeHistoricalData?: number; + useIntradayData?: boolean; }): Promise { - const quotes = await this.dataProviderService.getQuotes({ - items: [dataGatheringItem] - }); - const { currency, marketPrice } = quotes[dataGatheringItem.symbol] ?? {}; + let currency: string; + let marketPrice: number; + + if (useIntradayData) { + const latestMarketData = await this.marketDataService.getLatest({ + dataSource: dataGatheringItem.dataSource, + symbol: dataGatheringItem.symbol + }); + + marketPrice = latestMarketData?.marketPrice; + } else { + const quotes = await this.dataProviderService.getQuotes({ + items: [dataGatheringItem] + }); + + ({ currency, marketPrice } = quotes[dataGatheringItem.symbol] ?? {}); + } if (dataGatheringItem.dataSource && marketPrice >= 0) { let historicalData: HistoricalDataItem[] = []; diff --git a/apps/api/src/services/cron/cron.service.ts b/apps/api/src/services/cron/cron.service.ts index e680f0063..7299cbbf4 100644 --- a/apps/api/src/services/cron/cron.service.ts +++ b/apps/api/src/services/cron/cron.service.ts @@ -42,6 +42,7 @@ export class CronService { public async runEveryHourAtRandomMinute() { if (await this.isDataGatheringEnabled()) { await this.dataGatheringService.gather7Days(); + await this.dataGatheringService.gatherHourlySymbols(); } } diff --git a/apps/api/src/services/market-data/market-data.service.ts b/apps/api/src/services/market-data/market-data.service.ts index 87b08e1bd..27c741055 100644 --- a/apps/api/src/services/market-data/market-data.service.ts +++ b/apps/api/src/services/market-data/market-data.service.ts @@ -40,6 +40,19 @@ export class MarketDataService { }); } + public async getLatest({ + dataSource, + symbol + }: AssetProfileIdentifier): Promise { + return this.prismaService.marketData.findFirst({ + orderBy: [{ date: 'desc' }], + where: { + dataSource, + symbol + } + }); + } + public async getMax({ dataSource, symbol }: AssetProfileIdentifier) { return this.prismaService.marketData.findFirst({ select: { diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts index b5b701fe4..dfd371a13 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts @@ -2,6 +2,7 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data import { DataEnhancerInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-enhancer.interface'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; @@ -17,6 +18,7 @@ import { import { DATE_FORMAT, getAssetProfileIdentifier, + getStartOfUtcDate, resetHours } from '@ghostfolio/common/helper'; import { @@ -26,7 +28,7 @@ import { import { InjectQueue } from '@nestjs/bull'; import { Inject, Injectable, Logger } from '@nestjs/common'; -import { DataSource } from '@prisma/client'; +import { DataSource, Prisma } from '@prisma/client'; import { JobOptions, Queue } from 'bull'; import { format, min, subDays, subMilliseconds, subYears } from 'date-fns'; import { isEmpty } from 'lodash'; @@ -43,6 +45,7 @@ export class DataGatheringService { private readonly dataGatheringQueue: Queue, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, + private readonly marketDataService: MarketDataService, private readonly prismaService: PrismaService, private readonly propertyService: PropertyService, private readonly symbolProfileService: SymbolProfileService @@ -279,6 +282,46 @@ export class DataGatheringService { } } + public async gatherHourlySymbols() { + const assetProfileIdentifiers = + await this.getHourlyAssetProfileIdentifiers(); + + if (assetProfileIdentifiers.length <= 0) { + return; + } + + const date = getStartOfUtcDate(new Date()); + + try { + const quotes = await this.dataProviderService.getQuotes({ + items: assetProfileIdentifiers, + useCache: false + }); + + const data: Prisma.MarketDataUpdateInput[] = []; + + for (const { dataSource, symbol } of assetProfileIdentifiers) { + const quote = quotes[symbol]; + + if (quote?.dataSource !== dataSource || !quote.marketPrice) { + continue; + } + + data.push({ + dataSource, + date, + symbol, + marketPrice: quote.marketPrice, + state: 'INTRADAY' + }); + } + + await this.marketDataService.updateMany({ data }); + } catch (error) { + this.logger.error('Could not gather hourly market data', error); + } + } + public async gatherSymbols({ dataGatheringItems, force = false, @@ -389,6 +432,36 @@ export class DataGatheringService { return min([aStartDate, subYears(new Date(), 10)]); } + private async getHourlyAssetProfileIdentifiers(): Promise< + AssetProfileIdentifier[] + > { + const symbolProfiles = await this.prismaService.symbolProfile.findMany({ + orderBy: [{ symbol: 'asc' }, { dataSource: 'asc' }], + select: { + dataSource: true, + scraperConfiguration: true, + symbol: true + }, + where: { + dataGatheringFrequency: 'HOURLY', + isActive: true + } + }); + + return symbolProfiles + .filter(({ dataSource, scraperConfiguration }) => { + const manualDataSourceWithScraperConfiguration = + dataSource === 'MANUAL' && !isEmpty(scraperConfiguration); + + return ( + dataSource !== 'MANUAL' || manualDataSourceWithScraperConfiguration + ); + }) + .map(({ dataSource, symbol }) => { + return { dataSource, symbol }; + }); + } + private async getSymbols7D({ withUserSubscription = false }: { @@ -469,14 +542,12 @@ export class DataGatheringService { } }) ) - .filter((symbolProfile) => { + .filter(({ dataSource, scraperConfiguration }) => { const manualDataSourceWithScraperConfiguration = - symbolProfile.dataSource === 'MANUAL' && - !isEmpty(symbolProfile.scraperConfiguration); + dataSource === 'MANUAL' && !isEmpty(scraperConfiguration); return ( - symbolProfile.dataSource !== 'MANUAL' || - manualDataSourceWithScraperConfiguration + dataSource !== 'MANUAL' || manualDataSourceWithScraperConfiguration ); }) .map((symbolProfile) => { diff --git a/apps/api/src/services/symbol-profile/symbol-profile.service.ts b/apps/api/src/services/symbol-profile/symbol-profile.service.ts index 2d5116274..5d0968c5c 100644 --- a/apps/api/src/services/symbol-profile/symbol-profile.service.ts +++ b/apps/api/src/services/symbol-profile/symbol-profile.service.ts @@ -178,6 +178,7 @@ export class SymbolProfileService { comment, countries, currency, + dataGatheringFrequency, holdings, isActive, name, @@ -195,6 +196,7 @@ export class SymbolProfileService { comment, countries, currency, + dataGatheringFrequency, holdings, isActive, name, diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 829129b38..5ceb5808a 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -74,6 +74,7 @@ import { IonIcon } from '@ionic/angular/standalone'; import { AssetClass, AssetSubClass, + DataGatheringFrequency, MarketData, Prisma, SymbolProfile @@ -155,6 +156,7 @@ export class GfAssetProfileDialogComponent implements OnInit { comment: '', countries: ['', jsonValidator()], currency: '', + dataGatheringFrequency: new FormControl('DAILY'), historicalData: this.formBuilder.group({ csvString: '' }), @@ -199,6 +201,20 @@ export class GfAssetProfileDialogComponent implements OnInit { protected currencies: string[] = []; + protected readonly dataGatheringFrequencyValues: { + value: DataGatheringFrequency; + viewValue: string; + }[] = [ + { + value: 'DAILY', + viewValue: $localize`Daily` + }, + { + value: 'HOURLY', + viewValue: $localize`Hourly` + } + ]; + protected readonly dateRangeOptions = [ { label: $localize`Current week` + ' (' + $localize`WTD` + ')', @@ -401,6 +417,8 @@ export class GfAssetProfileDialogComponent implements OnInit { }) ?? [] ), currency: this.assetProfile?.currency ?? null, + dataGatheringFrequency: + this.assetProfile?.dataGatheringFrequency ?? 'DAILY', historicalData: { csvString: GfAssetProfileDialogComponent.HISTORICAL_DATA_TEMPLATE }, @@ -583,6 +601,9 @@ export class GfAssetProfileDialogComponent implements OnInit { this.assetProfileForm.controls.assetSubClass.value ?? undefined, comment: this.assetProfileForm.controls.comment.value || undefined, currency: this.assetProfileForm.controls.currency.value ?? undefined, + dataGatheringFrequency: + this.assetProfileForm.controls.dataGatheringFrequency.value ?? + undefined, isActive: isBoolean(this.assetProfileForm.controls.isActive.value) ? this.assetProfileForm.controls.isActive.value : undefined, diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index 453b8cb6a..f00c279a0 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -113,7 +113,7 @@
Overview
-
+
@if (isEditAssetProfileIdentifierMode) {
@@ -444,6 +444,21 @@ >
+
+ + Data Gathering Frequency + + @for ( + dataGatheringFrequencyValue of dataGatheringFrequencyValues; + track dataGatheringFrequencyValue.value + ) { + {{ + dataGatheringFrequencyValue.viewValue + }} + } + + +
diff --git a/libs/common/src/lib/dtos/update-asset-profile.dto.ts b/libs/common/src/lib/dtos/update-asset-profile.dto.ts index 1c8af3e72..ba3868482 100644 --- a/libs/common/src/lib/dtos/update-asset-profile.dto.ts +++ b/libs/common/src/lib/dtos/update-asset-profile.dto.ts @@ -1,6 +1,12 @@ import { IsCurrencyCode } from '@ghostfolio/common/validators/is-currency-code'; -import { AssetClass, AssetSubClass, DataSource, Prisma } from '@prisma/client'; +import { + AssetClass, + AssetSubClass, + DataGatheringFrequency, + DataSource, + Prisma +} from '@prisma/client'; import { IsArray, IsBoolean, @@ -32,6 +38,10 @@ export class UpdateAssetProfileDto { @IsOptional() currency?: string; + @IsEnum(DataGatheringFrequency) + @IsOptional() + dataGatheringFrequency?: DataGatheringFrequency; + @IsEnum(DataSource) @IsOptional() dataSource?: DataSource; diff --git a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts index 8426916c9..69fdf9f18 100644 --- a/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts +++ b/libs/common/src/lib/interfaces/enhanced-symbol-profile.interface.ts @@ -1,4 +1,9 @@ -import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; +import { + AssetClass, + AssetSubClass, + DataGatheringFrequency, + DataSource +} from '@prisma/client'; import { Country } from './country.interface'; import { DataProviderInfo } from './data-provider-info.interface'; @@ -15,6 +20,7 @@ export interface EnhancedSymbolProfile { createdAt: Date; currency?: string; cusip?: string; + dataGatheringFrequency?: DataGatheringFrequency; dataProviderInfo?: DataProviderInfo; dataSource: DataSource; dateOfFirstActivity?: Date; diff --git a/libs/common/src/lib/interfaces/responses/export-response.interface.ts b/libs/common/src/lib/interfaces/responses/export-response.interface.ts index fa592faf2..0e8ba1574 100644 --- a/libs/common/src/lib/interfaces/responses/export-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/export-response.interface.ts @@ -28,6 +28,7 @@ export interface ExportResponse { assetProfiles: (Omit< SymbolProfile, | 'createdAt' + | 'dataGatheringFrequency' | 'id' | 'scraperConfiguration' | 'symbolMapping' diff --git a/libs/ui/src/lib/services/admin.service.ts b/libs/ui/src/lib/services/admin.service.ts index 0eea768d6..90e477f28 100644 --- a/libs/ui/src/lib/services/admin.service.ts +++ b/libs/ui/src/lib/services/admin.service.ts @@ -189,6 +189,7 @@ export class AdminService { comment, countries, currency, + dataGatheringFrequency, dataSource: newDataSource, isActive, name, @@ -207,6 +208,7 @@ export class AdminService { comment, countries, currency, + dataGatheringFrequency, dataSource: newDataSource, isActive, name, diff --git a/prisma/migrations/20260620163851_added_data_gathering_frequency_to_symbol_profile/migration.sql b/prisma/migrations/20260620163851_added_data_gathering_frequency_to_symbol_profile/migration.sql new file mode 100644 index 000000000..33ea3f732 --- /dev/null +++ b/prisma/migrations/20260620163851_added_data_gathering_frequency_to_symbol_profile/migration.sql @@ -0,0 +1,8 @@ +-- CreateEnum +CREATE TYPE "DataGatheringFrequency" AS ENUM ('DAILY', 'HOURLY'); + +-- AlterTable +ALTER TABLE "SymbolProfile" ADD COLUMN "dataGatheringFrequency" "DataGatheringFrequency" NOT NULL DEFAULT 'DAILY'; + +-- CreateIndex +CREATE INDEX "SymbolProfile_dataGatheringFrequency_idx" ON "SymbolProfile"("dataGatheringFrequency"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 024ab7aa0..2c39667ee 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -191,6 +191,7 @@ model SymbolProfile { createdAt DateTime @default(now()) currency String cusip String? + dataGatheringFrequency DataGatheringFrequency @default(DAILY) dataSource DataSource figi String? figiComposite String? @@ -215,6 +216,7 @@ model SymbolProfile { @@index([assetClass]) @@index([currency]) @@index([cusip]) + @@index([dataGatheringFrequency]) @@index([dataSource]) @@index([isActive]) @@index([isin]) @@ -316,6 +318,11 @@ enum AssetSubClass { STOCK } +enum DataGatheringFrequency { + DAILY + HOURLY +} + enum DataSource { ALPHA_VANTAGE COINGECKO From 1587206e677ad32c7a74b6d629cdeddcce305b7a Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 20 Jun 2026 19:39:27 +0200 Subject: [PATCH 02/60] Bugfix/missing quotes for symbols across data sources (#7085) * Fix missing quotes * Update changelog --- CHANGELOG.md | 1 + .../endpoints/watchlist/watchlist.service.ts | 4 +- .../src/app/portfolio/current-rate.service.ts | 4 +- apps/api/src/app/symbol/symbol.service.ts | 8 +- .../services/benchmark/benchmark.service.ts | 8 +- .../data-provider/data-provider.service.ts | 73 +++++++++++++------ .../exchange-rate-data.service.ts | 9 ++- .../data-gathering/data-gathering.service.ts | 4 +- 8 files changed, 77 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 038612a02..a3433eeb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed an issue with the localization of the country names +- Fixed an issue in the data provider service where quotes could be missing for symbols that exist in multiple data sources by keying the quotes response by the asset profile identifier ## 3.12.0 - 2026-06-17 diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts index 666023dbf..791cc6c69 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts @@ -4,6 +4,7 @@ import { MarketDataService } from '@ghostfolio/api/services/market-data/market-d import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; +import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { WatchlistResponse } from '@ghostfolio/common/interfaces'; import { BadRequestException, Injectable } from '@nestjs/common'; @@ -127,7 +128,8 @@ export class WatchlistService { const performancePercent = this.benchmarkService.calculateChangeInPercentage( allTimeHigh?.marketPrice, - quotes[symbol]?.marketPrice + quotes[getAssetProfileIdentifier({ dataSource, symbol })] + ?.marketPrice ); return { diff --git a/apps/api/src/app/portfolio/current-rate.service.ts b/apps/api/src/app/portfolio/current-rate.service.ts index f0a451975..9cfeda3bd 100644 --- a/apps/api/src/app/portfolio/current-rate.service.ts +++ b/apps/api/src/app/portfolio/current-rate.service.ts @@ -51,13 +51,13 @@ export class CurrentRateService { const values: GetValueObject[] = []; if (includesToday) { - const quotesBySymbol = await this.dataProviderService.getQuotes({ + const quotes = await this.dataProviderService.getQuotes({ items: dataGatheringItems, user: this.request?.user }); for (const { dataSource, symbol } of dataGatheringItems) { - const quote = quotesBySymbol[symbol]; + const quote = quotes[getAssetProfileIdentifier({ dataSource, symbol })]; if (quote?.dataProviderInfo) { dataProviderInfos.push(quote.dataProviderInfo); diff --git a/apps/api/src/app/symbol/symbol.service.ts b/apps/api/src/app/symbol/symbol.service.ts index f2bf4beb1..732fcb23d 100644 --- a/apps/api/src/app/symbol/symbol.service.ts +++ b/apps/api/src/app/symbol/symbol.service.ts @@ -1,7 +1,10 @@ import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; -import { DATE_FORMAT } from '@ghostfolio/common/helper'; +import { + DATE_FORMAT, + getAssetProfileIdentifier +} from '@ghostfolio/common/helper'; import { DataProviderHistoricalResponse, HistoricalDataItem, @@ -46,7 +49,8 @@ export class SymbolService { items: [dataGatheringItem] }); - ({ currency, marketPrice } = quotes[dataGatheringItem.symbol] ?? {}); + ({ currency, marketPrice } = + quotes[getAssetProfileIdentifier(dataGatheringItem)] ?? {}); } if (dataGatheringItem.dataSource && marketPrice >= 0) { diff --git a/apps/api/src/services/benchmark/benchmark.service.ts b/apps/api/src/services/benchmark/benchmark.service.ts index 022a0e928..56a629163 100644 --- a/apps/api/src/services/benchmark/benchmark.service.ts +++ b/apps/api/src/services/benchmark/benchmark.service.ts @@ -8,7 +8,10 @@ import { CACHE_TTL_INFINITE, PROPERTY_BENCHMARKS } from '@ghostfolio/common/config'; -import { calculateBenchmarkTrend } from '@ghostfolio/common/helper'; +import { + calculateBenchmarkTrend, + getAssetProfileIdentifier +} from '@ghostfolio/common/helper'; import { AssetProfileIdentifier, Benchmark, @@ -266,8 +269,9 @@ export class BenchmarkService { let storeInCache = true; const benchmarks = allTimeHighs.map((allTimeHigh, index) => { + const { dataSource, symbol } = benchmarkAssetProfiles[index]; const { marketPrice } = - quotes[benchmarkAssetProfiles[index].symbol] ?? {}; + quotes[getAssetProfileIdentifier({ dataSource, symbol })] ?? {}; let performancePercentFromAllTimeHigh = 0; diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 5b54afb0b..fb9efff44 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -77,7 +77,9 @@ export class DataProviderService implements OnModuleInit { useCache: false }); - if (quotes[symbol]?.marketPrice > 0) { + if ( + quotes[getAssetProfileIdentifier({ dataSource, symbol })]?.marketPrice > 0 + ) { return true; } @@ -514,7 +516,6 @@ export class DataProviderService implements OnModuleInit { return result; } - // TODO: Change symbol in response to assetProfileIdentifier public async getQuotes({ items, requestTimeout, @@ -526,10 +527,12 @@ export class DataProviderService implements OnModuleInit { useCache?: boolean; user?: UserWithSettings; }): Promise<{ - [symbol: string]: DataProviderResponse; + [assetProfileIdentifier: string]: DataProviderResponse; }> { const response: { - [symbol: string]: DataProviderResponse; + [assetProfileIdentifier: string]: DataProviderResponse & { + symbol: string; + }; } = {}; const startTimeTotal = performance.now(); @@ -538,11 +541,17 @@ export class DataProviderService implements OnModuleInit { return symbol === `${DEFAULT_CURRENCY}USX`; }) ) { - response[`${DEFAULT_CURRENCY}USX`] = { + response[ + getAssetProfileIdentifier({ + dataSource: this.getDataSourceForExchangeRates(), + symbol: `${DEFAULT_CURRENCY}USX` + }) + ] = { currency: 'USX', dataSource: this.getDataSourceForExchangeRates(), marketPrice: 100, - marketState: 'open' + marketState: 'open', + symbol: `${DEFAULT_CURRENCY}USX` }; } @@ -557,8 +566,13 @@ export class DataProviderService implements OnModuleInit { if (quoteString) { try { - const cachedDataProviderResponse = JSON.parse(quoteString); - response[symbol] = cachedDataProviderResponse; + const cachedDataProviderResponse = JSON.parse( + quoteString + ) as DataProviderResponse; + response[getAssetProfileIdentifier({ dataSource, symbol })] = { + ...cachedDataProviderResponse, + symbol + }; continue; } catch {} } @@ -646,14 +660,19 @@ export class DataProviderService implements OnModuleInit { continue; } - response[symbol] = dataProviderResponse; + response[ + getAssetProfileIdentifier({ + symbol, + dataSource: DataSource[dataSource] + }) + ] = { ...dataProviderResponse, symbol }; this.redisCacheService.set( this.redisCacheService.getQuoteKey({ symbol, dataSource: DataSource[dataSource] }), - JSON.stringify(response[symbol]), + JSON.stringify(dataProviderResponse), this.configurationService.get('CACHE_QUOTES_TTL') ); @@ -663,7 +682,7 @@ export class DataProviderService implements OnModuleInit { rootCurrency } of DERIVED_CURRENCIES) { if (symbol === `${DEFAULT_CURRENCY}${rootCurrency}`) { - response[`${DEFAULT_CURRENCY}${currency}`] = { + const derivedDataProviderResponse: DataProviderResponse = { ...dataProviderResponse, currency, marketPrice: new Big( @@ -674,12 +693,22 @@ export class DataProviderService implements OnModuleInit { marketState: 'open' }; + response[ + getAssetProfileIdentifier({ + dataSource: DataSource[dataSource], + symbol: `${DEFAULT_CURRENCY}${currency}` + }) + ] = { + ...derivedDataProviderResponse, + symbol: `${DEFAULT_CURRENCY}${currency}` + }; + this.redisCacheService.set( this.redisCacheService.getQuoteKey({ dataSource: DataSource[dataSource], symbol: `${DEFAULT_CURRENCY}${currency}` }), - JSON.stringify(response[`${DEFAULT_CURRENCY}${currency}`]), + JSON.stringify(derivedDataProviderResponse), this.configurationService.get('CACHE_QUOTES_TTL') ); } @@ -697,21 +726,21 @@ export class DataProviderService implements OnModuleInit { try { await this.marketDataService.updateMany({ - data: Object.keys(response) - .filter((symbol) => { + data: Object.values(response) + .filter(({ marketPrice, marketState }) => { return ( - isNumber(response[symbol].marketPrice) && - response[symbol].marketPrice > 0 && - response[symbol].marketState === 'open' + isNumber(marketPrice) && + marketPrice > 0 && + marketState === 'open' ); }) - .map((symbol) => { + .map((dataProviderResponse) => { return { - symbol, - dataSource: response[symbol].dataSource, + dataSource: dataProviderResponse.dataSource, date: getStartOfUtcDate(new Date()), - marketPrice: response[symbol].marketPrice, - state: 'INTRADAY' + marketPrice: dataProviderResponse.marketPrice, + state: 'INTRADAY', + symbol: dataProviderResponse.symbol }; }) }); diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts index 708bfa591..d8a08c1c4 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts @@ -11,6 +11,7 @@ import { } from '@ghostfolio/common/config'; import { DATE_FORMAT, + getAssetProfileIdentifier, getYesterday, resetHours } from '@ghostfolio/common/helper'; @@ -176,11 +177,13 @@ export class ExchangeRateDataService { requestTimeout: ms('30 seconds') }); - for (const symbol of Object.keys(quotes)) { - if (isNumber(quotes[symbol].marketPrice)) { + for (const { dataSource, symbol } of this.currencyPairs) { + const quote = quotes[getAssetProfileIdentifier({ dataSource, symbol })]; + + if (isNumber(quote?.marketPrice)) { result[symbol] = { [format(getYesterday(), DATE_FORMAT)]: { - marketPrice: quotes[symbol].marketPrice + marketPrice: quote.marketPrice } }; } diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts index dfd371a13..789dea49b 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts @@ -301,9 +301,9 @@ export class DataGatheringService { const data: Prisma.MarketDataUpdateInput[] = []; for (const { dataSource, symbol } of assetProfileIdentifiers) { - const quote = quotes[symbol]; + const quote = quotes[getAssetProfileIdentifier({ dataSource, symbol })]; - if (quote?.dataSource !== dataSource || !quote.marketPrice) { + if (!quote?.marketPrice) { continue; } From 04a1c75365369513a4026d10bf7eb5823fab3127 Mon Sep 17 00:00:00 2001 From: Akash Negi <95514575+AkashNegi1@users.noreply.github.com> Date: Sat, 20 Jun 2026 23:35:24 +0530 Subject: [PATCH 03/60] Task/move market data endpoint for single asset to asset profiles (#7079) * Move market data endpoint for single asset to asset profiles * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/admin/admin.module.ts | 2 - apps/api/src/app/admin/admin.service.ts | 63 +------------------ apps/api/src/app/asset/asset.controller.ts | 11 +++- apps/api/src/app/asset/asset.module.ts | 4 +- .../asset-profiles.controller.ts | 58 ++++++++++++++++- .../asset-profiles/asset-profiles.module.ts | 11 +++- .../asset-profiles/asset-profiles.service.ts | 61 +++++++++++++++++- .../market-data/market-data.controller.ts | 55 +--------------- .../market-data/market-data.module.ts | 12 +--- libs/common/src/lib/interfaces/index.ts | 4 +- ...ts => asset-profile-response.interface.ts} | 2 +- libs/ui/src/lib/services/data.service.ts | 7 ++- 13 files changed, 149 insertions(+), 142 deletions(-) rename libs/common/src/lib/interfaces/responses/{market-data-details-response.interface.ts => asset-profile-response.interface.ts} (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3433eeb9..0c1c61f5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Changed the _Fear & Greed Index_ (market mood) in the markets overview to use the stored market data instead of a live quote - Moved the endpoint to get the asset profiles from `GET api/v1/admin/market-data` to `GET api/v1/asset-profiles` +- Moved the endpoint to get the asset profile details from `GET api/v1/market-data/:dataSource/:symbol` to `GET api/v1/asset-profiles/:dataSource/:symbol` - Added the selected asset profile count to the delete menu item of the historical market data table in the admin control panel - Added the selected asset profile count to the deletion confirmation dialog of the historical market data table in the admin control panel - Improved the sorting to be case-insensitive in the platform management of the admin control panel diff --git a/apps/api/src/app/admin/admin.module.ts b/apps/api/src/app/admin/admin.module.ts index 0cd4d3c16..2d5c734dc 100644 --- a/apps/api/src/app/admin/admin.module.ts +++ b/apps/api/src/app/admin/admin.module.ts @@ -1,4 +1,3 @@ -import { ActivitiesModule } from '@ghostfolio/api/app/activities/activities.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; @@ -19,7 +18,6 @@ import { QueueModule } from './queue/queue.module'; @Module({ imports: [ - ActivitiesModule, BenchmarkModule, ConfigurationModule, DataGatheringQueueModule, diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 9a5429655..8c0611c41 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -1,4 +1,3 @@ -import { ActivitiesService } from '@ghostfolio/api/app/activities/activities.service'; import { environment } from '@ghostfolio/api/environments/environment'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; @@ -12,14 +11,12 @@ import { PROPERTY_IS_READ_ONLY_MODE, PROPERTY_IS_USER_SIGNUP_ENABLED } from '@ghostfolio/common/config'; -import { getCurrencyFromSymbol, isCurrency } from '@ghostfolio/common/helper'; +import { getCurrencyFromSymbol } from '@ghostfolio/common/helper'; import { AdminData, - AdminMarketDataDetails, AdminUserResponse, AdminUsersResponse, - AssetProfileIdentifier, - EnhancedSymbolProfile + AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { @@ -42,7 +39,6 @@ import { StatusCodes, getReasonPhrase } from 'http-status-codes'; @Injectable() export class AdminService { public constructor( - private readonly activitiesService: ActivitiesService, private readonly configurationService: ConfigurationService, private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, @@ -176,61 +172,6 @@ export class AdminService { }; } - public async getMarketDataBySymbol({ - dataSource, - symbol - }: AssetProfileIdentifier): Promise { - let activitiesCount: EnhancedSymbolProfile['activitiesCount'] = 0; - let currency: EnhancedSymbolProfile['currency'] = '-'; - let dateOfFirstActivity: EnhancedSymbolProfile['dateOfFirstActivity']; - - const isCurrencyAssetProfile = isCurrency(getCurrencyFromSymbol(symbol)); - - if (isCurrencyAssetProfile) { - currency = getCurrencyFromSymbol(symbol); - ({ activitiesCount, dateOfFirstActivity } = - await this.activitiesService.getStatisticsByCurrency(currency)); - } - - const [[assetProfile], marketData] = await Promise.all([ - this.symbolProfileService.getSymbolProfiles([ - { - dataSource, - symbol - } - ]), - this.marketDataService.marketDataItems({ - orderBy: { - date: 'asc' - }, - where: { - dataSource, - symbol - } - }) - ]); - - if (assetProfile) { - assetProfile.dataProviderInfo = this.dataProviderService - .getDataProvider(assetProfile.dataSource) - .getDataProviderInfo(); - } - - return { - marketData, - assetProfile: assetProfile ?? { - activitiesCount, - currency, - dataSource, - dateOfFirstActivity, - symbol, - assetClass: isCurrencyAssetProfile ? AssetClass.LIQUIDITY : undefined, - assetSubClass: isCurrencyAssetProfile ? AssetSubClass.CASH : undefined, - isActive: true - } - }; - } - public async getUser(id: string): Promise { const [user] = await this.getUsersWithAnalytics({ where: { id } diff --git a/apps/api/src/app/asset/asset.controller.ts b/apps/api/src/app/asset/asset.controller.ts index 3b2031084..de04b5c81 100644 --- a/apps/api/src/app/asset/asset.controller.ts +++ b/apps/api/src/app/asset/asset.controller.ts @@ -1,4 +1,4 @@ -import { AdminService } from '@ghostfolio/api/app/admin/admin.service'; +import { AssetProfilesService } from '@ghostfolio/api/app/endpoints/asset-profiles/asset-profiles.service'; import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; import type { AssetResponse } from '@ghostfolio/common/interfaces'; @@ -9,7 +9,9 @@ import { pick } from 'lodash'; @Controller('asset') export class AssetController { - public constructor(private readonly adminService: AdminService) {} + public constructor( + private readonly assetProfilesService: AssetProfilesService + ) {} @Get(':dataSource/:symbol') @UseInterceptors(TransformDataSourceInRequestInterceptor) @@ -19,7 +21,10 @@ export class AssetController { @Param('symbol') symbol: string ): Promise { const { assetProfile, marketData } = - await this.adminService.getMarketDataBySymbol({ dataSource, symbol }); + await this.assetProfilesService.getAssetProfile({ + dataSource, + symbol + }); return { marketData, diff --git a/apps/api/src/app/asset/asset.module.ts b/apps/api/src/app/asset/asset.module.ts index 168585ed8..311c8694f 100644 --- a/apps/api/src/app/asset/asset.module.ts +++ b/apps/api/src/app/asset/asset.module.ts @@ -1,4 +1,4 @@ -import { AdminModule } from '@ghostfolio/api/app/admin/admin.module'; +import { AssetProfilesModule } from '@ghostfolio/api/app/endpoints/asset-profiles/asset-profiles.module'; import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; @@ -9,7 +9,7 @@ import { AssetController } from './asset.controller'; @Module({ controllers: [AssetController], imports: [ - AdminModule, + AssetProfilesModule, TransformDataSourceInRequestModule, TransformDataSourceInResponseModule ] diff --git a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.controller.ts b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.controller.ts index 2606d8075..fcddb0bc3 100644 --- a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.controller.ts +++ b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.controller.ts @@ -1,11 +1,17 @@ import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; +import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; +import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; import { ApiService } from '@ghostfolio/api/services/api/api.service'; +import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { UpdateAssetProfileDataDto } from '@ghostfolio/common/dtos'; +import { getCurrencyFromSymbol, isCurrency } from '@ghostfolio/common/helper'; +import { AssetProfileResponse } from '@ghostfolio/common/interfaces'; import { AssetProfilesResponse, EnhancedSymbolProfile } from '@ghostfolio/common/interfaces'; +import { hasPermission } from '@ghostfolio/common/permissions'; import { permissions } from '@ghostfolio/common/permissions'; import { MarketDataPreset, RequestWithUser } from '@ghostfolio/common/types'; @@ -18,7 +24,8 @@ import { Param, Patch, Query, - UseGuards + UseGuards, + UseInterceptors } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; @@ -32,7 +39,8 @@ export class AssetProfilesController { public constructor( private readonly apiService: ApiService, private readonly assetProfilesService: AssetProfilesService, - @Inject(REQUEST) private readonly request: RequestWithUser + @Inject(REQUEST) private readonly request: RequestWithUser, + private readonly symbolProfileService: SymbolProfileService ) {} @Get() @@ -64,6 +72,52 @@ export class AssetProfilesController { }); } + @Get(':dataSource/:symbol') + @UseGuards(AuthGuard('jwt')) + @UseInterceptors(TransformDataSourceInRequestInterceptor) + @UseInterceptors(TransformDataSourceInResponseInterceptor) + public async getAssetProfile( + @Param('dataSource') dataSource: DataSource, + @Param('symbol') symbol: string + ): Promise { + const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ + { dataSource, symbol } + ]); + + if (!assetProfile && !isCurrency(getCurrencyFromSymbol(symbol))) { + throw new HttpException( + getReasonPhrase(StatusCodes.NOT_FOUND), + StatusCodes.NOT_FOUND + ); + } + + const canReadAllAssetProfiles = hasPermission( + this.request.user.permissions, + permissions.readMarketData + ); + + const canReadOwnAssetProfile = + assetProfile?.userId === this.request.user.id && + hasPermission( + this.request.user.permissions, + permissions.readMarketDataOfOwnAssetProfile + ); + + if (!canReadAllAssetProfiles && !canReadOwnAssetProfile) { + throw new HttpException( + assetProfile?.userId + ? getReasonPhrase(StatusCodes.NOT_FOUND) + : getReasonPhrase(StatusCodes.FORBIDDEN), + assetProfile?.userId ? StatusCodes.NOT_FOUND : StatusCodes.FORBIDDEN + ); + } + + return this.assetProfilesService.getAssetProfile({ + dataSource, + symbol + }); + } + @HasPermission(permissions.accessAdminControl) @Patch(':dataSource/:symbol') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) diff --git a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.module.ts b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.module.ts index 98463ce5d..f96d92eb3 100644 --- a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.module.ts +++ b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.module.ts @@ -1,7 +1,11 @@ import { ActivitiesModule } from '@ghostfolio/api/app/activities/activities.module'; +import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; +import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; import { ApiModule } from '@ghostfolio/api/services/api/api.module'; import { BenchmarkModule } from '@ghostfolio/api/services/benchmark/benchmark.module'; +import { DataProviderModule } from '@ghostfolio/api/services/data-provider/data-provider.module'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; +import { MarketDataModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { PrismaModule } from '@ghostfolio/api/services/prisma/prisma.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; @@ -12,13 +16,18 @@ import { AssetProfilesService } from './asset-profiles.service'; @Module({ controllers: [AssetProfilesController], + exports: [AssetProfilesService], imports: [ ActivitiesModule, ApiModule, BenchmarkModule, + DataProviderModule, ExchangeRateDataModule, + MarketDataModule, PrismaModule, - SymbolProfileModule + SymbolProfileModule, + TransformDataSourceInRequestModule, + TransformDataSourceInResponseModule ], providers: [AssetProfilesService] }) diff --git a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts index 39eaa1642..3b0828868 100644 --- a/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts +++ b/apps/api/src/app/endpoints/asset-profiles/asset-profiles.service.ts @@ -1,6 +1,8 @@ import { ActivitiesService } from '@ghostfolio/api/app/activities/activities.service'; import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; +import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; +import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { UpdateAssetProfileDataDto } from '@ghostfolio/common/dtos'; @@ -11,8 +13,9 @@ import { isCurrency } from '@ghostfolio/common/helper'; import { - AssetProfileItem, + AdminMarketDataDetails, AssetProfileIdentifier, + AssetProfileItem, AssetProfilesResponse, EnhancedSymbolProfile, Filter @@ -28,11 +31,67 @@ export class AssetProfilesService { public constructor( private readonly activitiesService: ActivitiesService, private readonly benchmarkService: BenchmarkService, + private readonly dataProviderService: DataProviderService, private readonly exchangeRateDataService: ExchangeRateDataService, + private readonly marketDataService: MarketDataService, private readonly prismaService: PrismaService, private readonly symbolProfileService: SymbolProfileService ) {} + public async getAssetProfile({ + dataSource, + symbol + }: AssetProfileIdentifier): Promise { + let activitiesCount: EnhancedSymbolProfile['activitiesCount'] = 0; + let currency: EnhancedSymbolProfile['currency'] = '-'; + let dateOfFirstActivity: EnhancedSymbolProfile['dateOfFirstActivity']; + + const isCurrencyAssetProfile = isCurrency(getCurrencyFromSymbol(symbol)); + + if (isCurrencyAssetProfile) { + currency = getCurrencyFromSymbol(symbol); + ({ activitiesCount, dateOfFirstActivity } = + await this.activitiesService.getStatisticsByCurrency(currency)); + } + + const [[assetProfile], marketData] = await Promise.all([ + this.symbolProfileService.getSymbolProfiles([ + { + dataSource, + symbol + } + ]), + this.marketDataService.marketDataItems({ + orderBy: { + date: 'asc' + }, + where: { + dataSource, + symbol + } + }) + ]); + + if (assetProfile) { + assetProfile.dataProviderInfo = this.dataProviderService + .getDataProvider(assetProfile.dataSource) + .getDataProviderInfo(); + } + + return { + marketData, + assetProfile: assetProfile ?? { + activitiesCount, + currency, + dataSource, + dateOfFirstActivity, + symbol, + assetClass: isCurrencyAssetProfile ? AssetClass.LIQUIDITY : undefined, + assetSubClass: isCurrencyAssetProfile ? AssetSubClass.CASH : undefined, + isActive: true + } + }; + } public async getAssetProfiles({ filters = [], presetId, diff --git a/apps/api/src/app/endpoints/market-data/market-data.controller.ts b/apps/api/src/app/endpoints/market-data/market-data.controller.ts index f60eea904..0905a50bb 100644 --- a/apps/api/src/app/endpoints/market-data/market-data.controller.ts +++ b/apps/api/src/app/endpoints/market-data/market-data.controller.ts @@ -1,9 +1,6 @@ -import { AdminService } from '@ghostfolio/api/app/admin/admin.service'; import { SymbolService } from '@ghostfolio/api/app/symbol/symbol.service'; import { HasPermission } from '@ghostfolio/api/decorators/has-permission.decorator'; import { HasPermissionGuard } from '@ghostfolio/api/guards/has-permission.guard'; -import { TransformDataSourceInRequestInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor'; -import { TransformDataSourceInResponseInterceptor } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { @@ -14,10 +11,7 @@ import { } from '@ghostfolio/common/config'; import { UpdateBulkMarketDataDto } from '@ghostfolio/common/dtos'; import { getCurrencyFromSymbol, isCurrency } from '@ghostfolio/common/helper'; -import { - MarketDataDetailsResponse, - MarketDataOfMarketsResponse -} from '@ghostfolio/common/interfaces'; +import { MarketDataOfMarketsResponse } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { RequestWithUser } from '@ghostfolio/common/types'; @@ -30,8 +24,7 @@ import { Param, Post, Query, - UseGuards, - UseInterceptors + UseGuards } from '@nestjs/common'; import { REQUEST } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; @@ -42,7 +35,6 @@ import { getReasonPhrase, StatusCodes } from 'http-status-codes'; @Controller('market-data') export class MarketDataController { public constructor( - private readonly adminService: AdminService, private readonly marketDataService: MarketDataService, @Inject(REQUEST) private readonly request: RequestWithUser, private readonly symbolProfileService: SymbolProfileService, @@ -89,49 +81,6 @@ export class MarketDataController { }; } - @Get(':dataSource/:symbol') - @UseGuards(AuthGuard('jwt')) - @UseInterceptors(TransformDataSourceInRequestInterceptor) - @UseInterceptors(TransformDataSourceInResponseInterceptor) - public async getMarketDataBySymbol( - @Param('dataSource') dataSource: DataSource, - @Param('symbol') symbol: string - ): Promise { - const [assetProfile] = await this.symbolProfileService.getSymbolProfiles([ - { dataSource, symbol } - ]); - - if (!assetProfile && !isCurrency(getCurrencyFromSymbol(symbol))) { - throw new HttpException( - getReasonPhrase(StatusCodes.NOT_FOUND), - StatusCodes.NOT_FOUND - ); - } - - const canReadAllAssetProfiles = hasPermission( - this.request.user.permissions, - permissions.readMarketData - ); - - const canReadOwnAssetProfile = - assetProfile?.userId === this.request.user.id && - hasPermission( - this.request.user.permissions, - permissions.readMarketDataOfOwnAssetProfile - ); - - if (!canReadAllAssetProfiles && !canReadOwnAssetProfile) { - throw new HttpException( - assetProfile?.userId - ? getReasonPhrase(StatusCodes.NOT_FOUND) - : getReasonPhrase(StatusCodes.FORBIDDEN), - assetProfile?.userId ? StatusCodes.NOT_FOUND : StatusCodes.FORBIDDEN - ); - } - - return this.adminService.getMarketDataBySymbol({ dataSource, symbol }); - } - @Post(':dataSource/:symbol') @UseGuards(AuthGuard('jwt')) public async updateMarketData( diff --git a/apps/api/src/app/endpoints/market-data/market-data.module.ts b/apps/api/src/app/endpoints/market-data/market-data.module.ts index d5d64673d..1de10907b 100644 --- a/apps/api/src/app/endpoints/market-data/market-data.module.ts +++ b/apps/api/src/app/endpoints/market-data/market-data.module.ts @@ -1,7 +1,4 @@ -import { AdminModule } from '@ghostfolio/api/app/admin/admin.module'; import { SymbolModule } from '@ghostfolio/api/app/symbol/symbol.module'; -import { TransformDataSourceInRequestModule } from '@ghostfolio/api/interceptors/transform-data-source-in-request/transform-data-source-in-request.module'; -import { TransformDataSourceInResponseModule } from '@ghostfolio/api/interceptors/transform-data-source-in-response/transform-data-source-in-response.module'; import { MarketDataModule as MarketDataServiceModule } from '@ghostfolio/api/services/market-data/market-data.module'; import { SymbolProfileModule } from '@ghostfolio/api/services/symbol-profile/symbol-profile.module'; @@ -11,13 +8,6 @@ import { MarketDataController } from './market-data.controller'; @Module({ controllers: [MarketDataController], - imports: [ - AdminModule, - MarketDataServiceModule, - SymbolModule, - SymbolProfileModule, - TransformDataSourceInRequestModule, - TransformDataSourceInResponseModule - ] + imports: [MarketDataServiceModule, SymbolModule, SymbolProfileModule] }) export class MarketDataModule {} diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 989decdba..6777f8ac6 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -47,6 +47,7 @@ import type { AdminUsersResponse } from './responses/admin-users-response.interf import type { AiPromptResponse } from './responses/ai-prompt-response.interface'; import type { AiServiceHealthResponse } from './responses/ai-service-health-response.interface'; import type { ApiKeyResponse } from './responses/api-key-response.interface'; +import type { AssetProfileResponse } from './responses/asset-profile-response.interface'; import type { AssetProfilesResponse } from './responses/asset-profiles-response.interface'; import type { AssetResponse } from './responses/asset-response.interface'; import type { BenchmarkMarketDataDetailsResponse } from './responses/benchmark-market-data-details-response.interface'; @@ -67,7 +68,6 @@ import type { HistoricalResponse } from './responses/historical-response.interfa import type { ImportResponse } from './responses/import-response.interface'; import type { InfoResponse } from './responses/info-response.interface'; import type { LookupResponse } from './responses/lookup-response.interface'; -import type { MarketDataDetailsResponse } from './responses/market-data-details-response.interface'; import type { MarketDataOfMarketsResponse } from './responses/market-data-of-markets-response.interface'; import type { OAuthResponse } from './responses/oauth-response.interface'; import type { PlatformsResponse } from './responses/platforms-response.interface'; @@ -123,6 +123,7 @@ export { AssetClassSelectorOption, AssetProfileIdentifier, AssetProfileItem, + AssetProfileResponse, AssetProfilesResponse, AssetResponse, AttestationCredentialJSON, @@ -158,7 +159,6 @@ export { LookupItem, LookupResponse, MarketData, - MarketDataDetailsResponse, MarketDataOfMarketsResponse, NullableLineChartItem, OAuthResponse, diff --git a/libs/common/src/lib/interfaces/responses/market-data-details-response.interface.ts b/libs/common/src/lib/interfaces/responses/asset-profile-response.interface.ts similarity index 81% rename from libs/common/src/lib/interfaces/responses/market-data-details-response.interface.ts rename to libs/common/src/lib/interfaces/responses/asset-profile-response.interface.ts index bbf947301..884b0d411 100644 --- a/libs/common/src/lib/interfaces/responses/market-data-details-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/asset-profile-response.interface.ts @@ -2,7 +2,7 @@ import { MarketData } from '@prisma/client'; import { EnhancedSymbolProfile } from '../enhanced-symbol-profile.interface'; -export interface MarketDataDetailsResponse { +export interface AssetProfileResponse { assetProfile: Partial; marketData: MarketData[]; } diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index 079ea1bf6..ddd5f30b2 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -28,6 +28,7 @@ import { AiPromptResponse, ApiKeyResponse, AssetProfileIdentifier, + AssetProfileResponse, AssetProfilesResponse, AssetResponse, BenchmarkMarketDataDetailsResponse, @@ -40,7 +41,6 @@ import { ImportResponse, InfoItem, LookupResponse, - MarketDataDetailsResponse, MarketDataOfMarketsResponse, OAuthResponse, PlatformsResponse, @@ -544,14 +544,15 @@ export class DataService { }: { dataSource: DataSource; symbol: string; - }): Observable { + }): Observable { return this.http - .get(`/api/v1/market-data/${dataSource}/${symbol}`) + .get(`/api/v1/asset-profiles/${dataSource}/${symbol}`) .pipe( map((data) => { for (const item of data.marketData) { item.date = parseISO(item.date); } + return data; }) ); From 600e2435dd734bb3004c68edfa35c3138b931122 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sat, 20 Jun 2026 20:11:07 +0200 Subject: [PATCH 04/60] Release 3.13.0 (#7088) --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c1c61f5b..7c3558ccc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 3.13.0 - 2026-06-20 ### Added diff --git a/package-lock.json b/package-lock.json index 0fceee455..30cb9b2aa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "3.12.0", + "version": "3.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "3.12.0", + "version": "3.13.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 6ec921f21..1003366da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "3.12.0", + "version": "3.13.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From 55bb02f0f8ccb2f5c2a6e5fd69850d20e28425dc Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 21 Jun 2026 09:16:30 +0200 Subject: [PATCH 05/60] Task/various improvements in subscription table of user detail dialog (#7091) Various improvements --- apps/api/src/app/admin/admin.service.ts | 9 +------ .../user-detail-dialog.component.ts | 10 +++++--- .../user-detail-dialog.html | 24 +++++++++++-------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 8c0611c41..6dddbbca7 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -182,7 +182,7 @@ export class AdminService { } if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { - const subscriptions = await this.prismaService.subscription.findMany({ + user.subscriptions = await this.prismaService.subscription.findMany({ orderBy: { expiresAt: 'desc' }, @@ -190,13 +190,6 @@ export class AdminService { userId: id } }); - - user.subscriptions = subscriptions.map((subscription) => { - return { - ...subscription, - price: subscription.price ?? 0 - }; - }); } return user; diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts index 7b008786d..f4e38c77a 100644 --- a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts @@ -106,9 +106,13 @@ export class GfUserDetailDialogComponent implements OnInit { public getSum() { return getSum( - this.subscriptionsDataSource.data.map(({ price }) => { - return new Big(price); - }) + this.subscriptionsDataSource.data + .filter(({ price }) => { + return price !== null; + }) + .map(({ price }) => { + return new Big(price); + }) ).toNumber(); } diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html index df935c780..18bd00a41 100644 --- a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html @@ -128,7 +128,7 @@ @if (subscriptionsDataSource.data.length > 0) {
-

Subscriptions

+

Subscription History

@@ -63,31 +63,31 @@ - - + + - - + + - - + + - @if (product1.note || product2.note) { + @if (product1().note || product2().note) { - - + + } @@ -310,9 +310,9 @@

Please note that the information provided in the Ghostfolio vs - {{ product2.name }} comparison table is based on our independent + {{ product2().name }} comparison table is based on our independent research and analysis. This website is not affiliated with - {{ product2.name }} or any other product mentioned in the + {{ product2().name }} or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain @@ -337,7 +337,7 @@

    - @for (tag of tags; track tag) { + @for (tag of tags(); track tag) {
  • {{ tag }}
  • @@ -355,7 +355,7 @@ aria-current="page" class="active breadcrumb-item text-truncate" > - Ghostfolio vs {{ product2.name }} + Ghostfolio vs {{ product2().name }} From e66df158e949d0bdda3cfcf15d7c80dfd30a111e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:21:00 +0200 Subject: [PATCH 23/60] Task/extend personal finance tools (20260623) (#7112) Extend personal finance tools --- libs/common/src/lib/personal-finance-tools.ts | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/libs/common/src/lib/personal-finance-tools.ts b/libs/common/src/lib/personal-finance-tools.ts index 744081464..265d1b6b9 100644 --- a/libs/common/src/lib/personal-finance-tools.ts +++ b/libs/common/src/lib/personal-finance-tools.ts @@ -438,6 +438,17 @@ export const personalFinanceTools: Product[] = [ slogan: 'Portfolio Tracker für dein Vermögen', url: 'https://www.finanzfluss.de/copilot' }, + { + founded: 2015, + hasFreePlan: true, + hasSelfHostingAbility: false, + key: 'finanzguru', + languages: ['Deutsch'], + name: 'Finanzguru', + origin: 'DE', + slogan: 'Finanzen. Magisch. Einfach.', + url: 'https://www.finanzguru.de' + }, { founded: 2020, key: 'finary', @@ -611,6 +622,15 @@ export const personalFinanceTools: Product[] = [ slogan: 'Finance App for Couples', url: 'https://www.honeydue.com' }, + { + founded: 2003, + hasSelfHostingAbility: true, + isArchived: true, + key: 'ibank', + name: 'iBank', + note: 'Renamed to Banktivity', + origin: 'US' + }, { founded: 2022, key: 'income-reign', @@ -817,6 +837,17 @@ export const personalFinanceTools: Product[] = [ slogan: 'Personal Finance Manager for Mac, Windows, and Linux', url: 'https://moneydance.com' }, + { + founded: 2016, + hasFreePlan: true, + hasSelfHostingAbility: false, + key: 'moneypatrol', + name: 'MoneyPatrol', + origin: 'US', + pricingPerYear: '$29.99', + slogan: 'Personal Accounting Made Easy, Accurate, Comprehensive', + url: 'https://www.moneypatrol.com' + }, { hasFreePlan: true, hasSelfHostingAbility: false, @@ -1390,6 +1421,26 @@ export const personalFinanceTools: Product[] = [ pricingPerYear: '$600', slogan: 'Make Smarter Investments' }, + { + founded: 2020, + hasFreePlan: true, + hasSelfHostingAbility: false, + key: 'wallstreetzen', + name: 'WallStreetZen', + origin: 'HK', + pricingPerYear: '$234', + slogan: 'Stock market analysis for the serious part-time investor', + url: 'https://www.wallstreetzen.com' + }, + { + founded: 2015, + hasSelfHostingAbility: false, + key: 'wealtharc', + name: 'WealthArc', + origin: 'CH', + slogan: 'Manage wealth data more efficiently', + url: 'https://www.wealtharc.com' + }, { founded: 2019, hasFreePlan: false, From 09486ef56efedbe5beaabbcbfa9b67ac34ad652e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 17:21:26 +0200 Subject: [PATCH 24/60] Task/harmonize price column in subscription table of user detail dialog (#7108) Harmonize price column --- .../app/components/user-detail-dialog/user-detail-dialog.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html index 18bd00a41..0b0b59e7d 100644 --- a/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html +++ b/apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html @@ -171,7 +171,8 @@ mat-cell > @if (element.price === null) { - + + {{ baseCurrency }} } @else { Date: Tue, 23 Jun 2026 17:21:55 +0200 Subject: [PATCH 25/60] Bugfix/disabled state of delete action menu item in market data of admin control panel (#7111) * Fix disabled state * Update changelog --- CHANGELOG.md | 6 ++++++ .../app/components/admin-market-data/admin-market-data.html | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d72694758..bccd353cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Fixed the disabled state of the delete action in the asset profiles actions menu of the historical market data table in the admin control panel + ## 3.14.0 - 2026-06-22 ### Added diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.html b/apps/client/src/app/components/admin-market-data/admin-market-data.html index 270722a4b..83703b548 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.html +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.html @@ -285,7 +285,8 @@ !canDeleteAssetProfile({ activitiesCount: element.activitiesCount, isBenchmark: element.isBenchmark, - symbol: element.symbol + symbol: element.symbol, + watchedByCount: element.watchedByCount }) " (click)=" From 608f9673b46cc17e31c45112935d2ff67df588f4 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:43:58 +0200 Subject: [PATCH 26/60] Task/improve dynamic numerical precision for various values in account and holding detail dialogs on mobile (#7113) * Improve dynamic numerical precision * Update changelog --- CHANGELOG.md | 5 +++++ .../account-detail-dialog.component.ts | 10 +++++----- .../holding-detail-dialog.component.ts | 16 ++++++++-------- libs/common/src/lib/config.ts | 1 + 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bccd353cf..28c2b25c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Changed + +- Improved the dynamic numerical precision for various values in the account detail dialog on mobile +- Improved the dynamic numerical precision for various values in the holding detail dialog on mobile + ### Fixed - Fixed the disabled state of the delete action in the asset profiles actions menu of the historical market data table in the admin control panel diff --git a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts index 7cdf3e671..5af8c9d00 100644 --- a/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts +++ b/apps/client/src/app/components/account-detail-dialog/account-detail-dialog.component.ts @@ -3,7 +3,7 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; import { DEFAULT_DATE_RANGE, DEFAULT_PAGE_SIZE, - NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + NUMERICAL_PRECISION_THRESHOLD_4_FIGURES } from '@ghostfolio/common/config'; import { CreateAccountBalanceDto } from '@ghostfolio/common/dtos'; import { DATE_FORMAT, downloadAsFile } from '@ghostfolio/common/helper'; @@ -245,7 +245,7 @@ export class GfAccountDetailDialogComponent implements OnInit { this.balance = balance; if ( - this.balance >= NUMERICAL_PRECISION_THRESHOLD_5_FIGURES && + this.balance >= NUMERICAL_PRECISION_THRESHOLD_4_FIGURES && this.data.deviceType === 'mobile' ) { this.balancePrecision = 0; @@ -257,7 +257,7 @@ export class GfAccountDetailDialogComponent implements OnInit { if ( this.data.deviceType === 'mobile' && this.dividendInBaseCurrency >= - NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.dividendInBaseCurrencyPrecision = 0; } @@ -267,7 +267,7 @@ export class GfAccountDetailDialogComponent implements OnInit { if ( this.data.deviceType === 'mobile' && - this.equity >= NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + this.equity >= NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.equityPrecision = 0; } @@ -280,7 +280,7 @@ export class GfAccountDetailDialogComponent implements OnInit { if ( this.data.deviceType === 'mobile' && this.interestInBaseCurrency >= - NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.interestInBaseCurrencyPrecision = 0; } diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index 1eb0d4ab6..e14c638be 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -2,7 +2,7 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; import { DEFAULT_PAGE_SIZE, NUMERICAL_PRECISION_THRESHOLD_3_FIGURES, - NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + NUMERICAL_PRECISION_THRESHOLD_4_FIGURES } from '@ghostfolio/common/config'; import { CreateOrderDto } from '@ghostfolio/common/dtos'; import { @@ -288,7 +288,7 @@ export class GfHoldingDetailDialogComponent implements OnInit { this.averagePrice = averagePrice; if ( - this.averagePrice >= NUMERICAL_PRECISION_THRESHOLD_5_FIGURES && + this.averagePrice >= NUMERICAL_PRECISION_THRESHOLD_4_FIGURES && this.data.deviceType === 'mobile' ) { this.averagePricePrecision = 0; @@ -307,7 +307,7 @@ export class GfHoldingDetailDialogComponent implements OnInit { if ( this.data.deviceType === 'mobile' && this.dividendInBaseCurrency >= - NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.dividendInBaseCurrencyPrecision = 0; } @@ -345,7 +345,7 @@ export class GfHoldingDetailDialogComponent implements OnInit { if ( this.data.deviceType === 'mobile' && this.investmentInBaseCurrencyWithCurrencyEffect >= - NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.investmentInBaseCurrencyWithCurrencyEffectPrecision = 0; } @@ -355,7 +355,7 @@ export class GfHoldingDetailDialogComponent implements OnInit { if ( this.data.deviceType === 'mobile' && - this.marketPriceMax >= NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + this.marketPriceMax >= NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.marketPriceMaxPrecision = 0; } @@ -364,14 +364,14 @@ export class GfHoldingDetailDialogComponent implements OnInit { if ( this.data.deviceType === 'mobile' && - this.marketPriceMin >= NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + this.marketPriceMin >= NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.marketPriceMinPrecision = 0; } if ( this.data.deviceType === 'mobile' && - this.marketPrice >= NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + this.marketPrice >= NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.marketPricePrecision = 0; } @@ -393,7 +393,7 @@ export class GfHoldingDetailDialogComponent implements OnInit { if ( this.data.deviceType === 'mobile' && this.netPerformanceWithCurrencyEffect >= - NUMERICAL_PRECISION_THRESHOLD_5_FIGURES + NUMERICAL_PRECISION_THRESHOLD_4_FIGURES ) { this.netPerformanceWithCurrencyEffectPrecision = 0; } diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 40412d627..e6d717c7b 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -231,6 +231,7 @@ export const HEADER_KEY_SKIP_INTERCEPTOR = 'X-Skip-Interceptor'; export const MAX_TOP_HOLDINGS = 50; export const NUMERICAL_PRECISION_THRESHOLD_3_FIGURES = 100; +export const NUMERICAL_PRECISION_THRESHOLD_4_FIGURES = 1000; export const NUMERICAL_PRECISION_THRESHOLD_5_FIGURES = 10000; export const NUMERICAL_PRECISION_THRESHOLD_6_FIGURES = 100000; From 4996ad6425bb73c1bb6b04ab5a0ccb62c7810948 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:44:51 +0200 Subject: [PATCH 27/60] Bugfix/handle empty locale string in scraper configuration (#7109) * Handle empty locale string in scraper configuration * Update changelog --- CHANGELOG.md | 1 + .../asset-profile-dialog/asset-profile-dialog.component.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28c2b25c4..0faaca8d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed the disabled state of the delete action in the asset profiles actions menu of the historical market data table in the admin control panel +- Fixed the persistence of an empty `locale` string in the scraper configuration ## 3.14.0 - 2026-06-22 diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts index 5ceb5808a..967a289ef 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts @@ -550,7 +550,7 @@ export class GfAssetProfileDialogComponent implements OnInit { ) as Record, locale: this.assetProfileForm.controls.scraperConfiguration.controls.locale - ?.value ?? undefined, + ?.value || undefined, mode: this.assetProfileForm.controls.scraperConfiguration.controls.mode ?.value ?? undefined, From f53998f071af33d02205028762f19e6dc31f07e2 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:46:16 +0200 Subject: [PATCH 28/60] Bugfix/encode symbols with special characters in API request urls (#7110) * Encode symbols in API request urls * Update changelog --- CHANGELOG.md | 1 + .../src/app/pages/api/api-page.component.ts | 6 +-- libs/ui/src/lib/services/admin.service.ts | 14 +++--- libs/ui/src/lib/services/data.service.ts | 49 +++++++++++-------- 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0faaca8d0..4103287cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed an issue where symbols with special characters caused API request failures by URL encoding the symbol - Fixed the disabled state of the delete action in the asset profiles actions menu of the historical market data table in the admin control panel - Fixed the persistence of an empty `locale` string in the scraper configuration diff --git a/apps/client/src/app/pages/api/api-page.component.ts b/apps/client/src/app/pages/api/api-page.component.ts index e75a51c73..b949cfb0f 100644 --- a/apps/client/src/app/pages/api/api-page.component.ts +++ b/apps/client/src/app/pages/api/api-page.component.ts @@ -94,7 +94,7 @@ export class GfApiPageComponent implements OnInit { private fetchAssetProfile({ symbol }: { symbol: string }) { return this.http .get( - `/api/v1/data-providers/ghostfolio/asset-profile/${symbol}`, + `/api/v1/data-providers/ghostfolio/asset-profile/${encodeURIComponent(symbol)}`, { headers: this.getHeaders() } ) .pipe(this.catchFetchFailure(), takeUntilDestroyed(this.destroyRef)); @@ -107,7 +107,7 @@ export class GfApiPageComponent implements OnInit { return this.http .get( - `/api/v2/data-providers/ghostfolio/dividends/${symbol}`, + `/api/v2/data-providers/ghostfolio/dividends/${encodeURIComponent(symbol)}`, { params, headers: this.getHeaders() @@ -129,7 +129,7 @@ export class GfApiPageComponent implements OnInit { return this.http .get( - `/api/v2/data-providers/ghostfolio/historical/${symbol}`, + `/api/v2/data-providers/ghostfolio/historical/${encodeURIComponent(symbol)}`, { params, headers: this.getHeaders() diff --git a/libs/ui/src/lib/services/admin.service.ts b/libs/ui/src/lib/services/admin.service.ts index c767c4f48..4d2f6e48f 100644 --- a/libs/ui/src/lib/services/admin.service.ts +++ b/libs/ui/src/lib/services/admin.service.ts @@ -36,7 +36,7 @@ export class AdminService { public addAssetProfile({ dataSource, symbol }: AssetProfileIdentifier) { return this.http.post( - `/api/v1/admin/profile-data/${dataSource}/${symbol}`, + `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}`, null ); } @@ -63,7 +63,7 @@ export class AdminService { public deleteProfileData({ dataSource, symbol }: AssetProfileIdentifier) { return this.http.delete( - `/api/v1/admin/profile-data/${dataSource}/${symbol}` + `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}` ); } @@ -140,7 +140,7 @@ export class AdminService { symbol }: AssetProfileIdentifier) { return this.http.post( - `/api/v1/admin/gather/profile-data/${dataSource}/${symbol}`, + `/api/v1/admin/gather/profile-data/${dataSource}/${encodeURIComponent(symbol)}`, {} ); } @@ -162,7 +162,7 @@ export class AdminService { params = params.append('range', range); } - const url = `/api/v1/admin/gather/${dataSource}/${symbol}`; + const url = `/api/v1/admin/gather/${dataSource}/${encodeURIComponent(symbol)}`; return this.http.post(url, undefined, { params }); } @@ -172,7 +172,7 @@ export class AdminService { dateString, symbol }: { dateString: string } & AssetProfileIdentifier) { - const url = `/api/v1/symbol/${dataSource}/${symbol}/${dateString}`; + const url = `/api/v1/symbol/${dataSource}/${encodeURIComponent(symbol)}/${dateString}`; return this.http.get(url); } @@ -197,7 +197,7 @@ export class AdminService { }: UpdateAssetProfileDto ) { return this.http.patch( - `/api/v1/admin/profile-data/${dataSource}/${symbol}`, + `/api/v1/admin/profile-data/${dataSource}/${encodeURIComponent(symbol)}`, { assetClass, assetSubClass, @@ -238,7 +238,7 @@ export class AdminService { symbol }: AssetProfileIdentifier & UpdateAssetProfileDto['scraperConfiguration']) { return this.http.post<{ price: number }>( - `/api/v1/admin/market-data/${dataSource}/${symbol}/test`, + `/api/v1/admin/market-data/${dataSource}/${encodeURIComponent(symbol)}/test`, { scraperConfiguration } diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index 8d16c6d35..f49beab23 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -301,7 +301,7 @@ export class DataService { public fetchDividendsImport({ dataSource, symbol }: AssetProfileIdentifier) { return this.http.get( - `/api/v1/import/dividends/${dataSource}/${symbol}` + `/api/v1/import/dividends/${dataSource}/${encodeURIComponent(symbol)}` ); } @@ -313,7 +313,7 @@ export class DataService { symbol: string; }) { return this.http.get( - `/api/v1/exchange-rate/${symbol}/${format(date, DATE_FORMAT, { in: utc })}` + `/api/v1/exchange-rate/${encodeURIComponent(symbol)}/${format(date, DATE_FORMAT, { in: utc })}` ); } @@ -341,7 +341,7 @@ export class DataService { public deleteBenchmark({ dataSource, symbol }: AssetProfileIdentifier) { return this.http.delete>( - `/api/v1/benchmarks/${dataSource}/${symbol}` + `/api/v1/benchmarks/${dataSource}/${encodeURIComponent(symbol)}` ); } @@ -358,7 +358,9 @@ export class DataService { } public deleteWatchlistItem({ dataSource, symbol }: AssetProfileIdentifier) { - return this.http.delete(`/api/v1/watchlist/${dataSource}/${symbol}`); + return this.http.delete( + `/api/v1/watchlist/${dataSource}/${encodeURIComponent(symbol)}` + ); } public fetchAccesses() { @@ -369,14 +371,16 @@ export class DataService { dataSource, symbol }: AssetProfileIdentifier): Observable { - return this.http.get(`/api/v1/asset/${dataSource}/${symbol}`).pipe( - map((data) => { - for (const item of data.marketData) { - item.date = parseISO(item.date); - } - return data; - }) - ); + return this.http + .get(`/api/v1/asset/${dataSource}/${encodeURIComponent(symbol)}`) + .pipe( + map((data) => { + for (const item of data.marketData) { + item.date = parseISO(item.date); + } + return data; + }) + ); } public fetchAssetProfiles({ @@ -437,7 +441,7 @@ export class DataService { } return this.http.get( - `/api/v1/benchmarks/${dataSource}/${symbol}/${format(startDate, DATE_FORMAT, { in: utc })}`, + `/api/v1/benchmarks/${dataSource}/${encodeURIComponent(symbol)}/${format(startDate, DATE_FORMAT, { in: utc })}`, { params } ); } @@ -486,7 +490,7 @@ export class DataService { > { return this.http .get( - `/api/v1/portfolio/holding/${dataSource}/${symbol}` + `/api/v1/portfolio/holding/${dataSource}/${encodeURIComponent(symbol)}` ) .pipe( map((response) => { @@ -540,7 +544,9 @@ export class DataService { symbol }: AssetProfileIdentifier): Observable { return this.http - .get(`/api/v1/asset-profiles/${dataSource}/${symbol}`) + .get( + `/api/v1/asset-profiles/${dataSource}/${encodeURIComponent(symbol)}` + ) .pipe( map((data) => { for (const item of data.marketData) { @@ -778,9 +784,12 @@ export class DataService { params = params.append('includeHistoricalData', includeHistoricalData); } - return this.http.get(`/api/v1/symbol/${dataSource}/${symbol}`, { - params - }); + return this.http.get( + `/api/v1/symbol/${dataSource}/${encodeURIComponent(symbol)}`, + { + params + } + ); } public fetchSymbols({ @@ -851,7 +860,7 @@ export class DataService { marketData, symbol }: { marketData: UpdateBulkMarketDataDto } & AssetProfileIdentifier) { - const url = `/api/v1/market-data/${dataSource}/${symbol}`; + const url = `/api/v1/market-data/${dataSource}/${encodeURIComponent(symbol)}`; return this.http.post(url, marketData); } @@ -890,7 +899,7 @@ export class DataService { tags }: { tags: Tag[] } & AssetProfileIdentifier) { return this.http.put( - `/api/v1/portfolio/holding/${dataSource}/${symbol}/tags`, + `/api/v1/portfolio/holding/${dataSource}/${encodeURIComponent(symbol)}/tags`, { tags } ); } From 9db16e9d2c5aaf99f5fa7a5a45495b5f4c05d65d Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:47:57 +0200 Subject: [PATCH 29/60] Task/increase default limit of getJobs() in queue service (#7105) Increase default limit of getJobs() --- apps/api/src/app/admin/queue/queue.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/src/app/admin/queue/queue.service.ts b/apps/api/src/app/admin/queue/queue.service.ts index f47b3d3a1..d31834eea 100644 --- a/apps/api/src/app/admin/queue/queue.service.ts +++ b/apps/api/src/app/admin/queue/queue.service.ts @@ -56,7 +56,7 @@ export class QueueService { } public async getJobs({ - limit = 1000, + limit = 5000, status = QUEUE_JOB_STATUS_LIST }: { limit?: number; From 944846c51f1c010c8f2f786dc9522bb3aa0c9bb1 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:48:37 +0200 Subject: [PATCH 30/60] Task/upgrade @internationalized/number to version 3.6.7 (#7114) * Upgrade @internationalized/number to version 3.6.7 * Update changelog --- CHANGELOG.md | 1 + package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4103287cd..63ac12cb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the dynamic numerical precision for various values in the account detail dialog on mobile - Improved the dynamic numerical precision for various values in the holding detail dialog on mobile +- Upgraded `@internationalized/number` from version `3.6.6` to `3.6.7` ### Fixed diff --git a/package-lock.json b/package-lock.json index 467dfcd0b..14bc7c595 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "@bull-board/nestjs": "7.2.1", "@codewithdan/observable-store": "2.2.15", "@date-fns/utc": "2.1.1", - "@internationalized/number": "3.6.6", + "@internationalized/number": "3.6.7", "@ionic/angular": "8.8.5", "@keyv/redis": "5.1.6", "@nestjs/bull": "11.0.4", @@ -5003,9 +5003,9 @@ } }, "node_modules/@internationalized/number": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.6.tgz", - "integrity": "sha512-iFgmQaXHE0vytNfpLZWOC2mEJCBRzcUxt53Xf/yCXG93lRvqas237i3r7X4RKMwO3txiyZD4mQjKAByFv6UGSQ==", + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.7.tgz", + "integrity": "sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg==", "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" diff --git a/package.json b/package.json index 7166ecd46..6b7c5a1c2 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "@bull-board/nestjs": "7.2.1", "@codewithdan/observable-store": "2.2.15", "@date-fns/utc": "2.1.1", - "@internationalized/number": "3.6.6", + "@internationalized/number": "3.6.7", "@ionic/angular": "8.8.5", "@keyv/redis": "5.1.6", "@nestjs/bull": "11.0.4", From bfa42950ad91a389cf210c1f50bab788b5649572 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:51:11 +0200 Subject: [PATCH 31/60] Task/update locales (#7107) Co-authored-by: github-actions[bot] --- apps/client/src/locales/messages.ca.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.de.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.es.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.fr.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.it.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.ko.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.nl.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.pl.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.pt.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.tr.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.uk.xlf | 92 ++++++++++++------------- apps/client/src/locales/messages.xlf | 66 +++++++++--------- apps/client/src/locales/messages.zh.xlf | 92 ++++++++++++------------- 13 files changed, 585 insertions(+), 585 deletions(-) diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index 76caa9c1e..9d17d4bc1 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -607,7 +607,7 @@ Suprimir apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -1655,7 +1655,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -4688,7 +4688,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -5008,7 +5008,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -5181,7 +5181,7 @@ Global apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5197,24 +5197,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Estàs buscant una alternativa de codi obert a ? Ghostfolio és una potent eina de gestió de portafolis que proporciona a les persones una plataforma completa per fer el seguiment, analitzar i optimitzar les seves inversions. Tant si ets un inversor amb experiència com si tot just comences, Ghostfolio ofereix una interfície intuïtiva i una àmplia gamma de funcionalitats per ajudar-te a prendre decisions informades i a prendre el control del teu futur financer. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Estàs buscant una alternativa de codi obert a ? Ghostfolio és una potent eina de gestió de portafolis que proporciona a les persones una plataforma completa per fer el seguiment, analitzar i optimitzar les seves inversions. Tant si ets un inversor amb experiència com si tot just comences, Ghostfolio ofereix una interfície intuïtiva i una àmplia gamma de funcionalitats per ajudar-te a prendre decisions informades i a prendre el control del teu futur financer. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio és un programari de codi obert (OSS), que proporciona una alternativa rendible a , especialment adequada per a persones amb un pressupost ajustat, com ara aquelles que segueixen el camí cap a la independència financera i jubilació anticipada (FIRE). Mitjançant els esforços col·lectius d’una comunitat de desenvolupadors i apassionats de les finances personals, Ghostfolio millora contínuament les seves capacitats, la seva seguretat i l’experiència d’usuari. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio és un programari de codi obert (OSS), que proporciona una alternativa rendible a , especialment adequada per a persones amb un pressupost ajustat, com ara aquelles que segueixen el camí cap a la independència financera i jubilació anticipada (FIRE). Mitjançant els esforços col·lectius d’una comunitat de desenvolupadors i apassionats de les finances personals, Ghostfolio millora contínuament les seves capacitats, la seva seguretat i l’experiència d’usuari. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Explorem en profunditat la taula comparativa detallada de Ghostfolio vs que trobaràs a continuació per entendre a fons com es posiciona Ghostfolio en relació amb . Analitzarem diversos aspectes com ara funcionalitats, privadesa de dades, preus i molt més, per tal que puguis prendre una decisió ben informada segons les teves necessitats personals. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Explorem en profunditat la taula comparativa detallada de Ghostfolio vs que trobaràs a continuació per entendre a fons com es posiciona Ghostfolio en relació amb . Analitzarem diversos aspectes com ara funcionalitats, privadesa de dades, preus i molt més, per tal que puguis prendre una decisió ben informada segons les teves necessitats personals. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5233,8 +5233,8 @@ - Ghostfolio vs comparison table - Taula comparativa Ghostfolio vs + Ghostfolio vs comparison table + Taula comparativa Ghostfolio vs apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -5397,8 +5397,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Tingues en compte que la informació proporcionada a la taula comparativa Ghostfolio vs es basa en la nostra investigació i anàlisi independents. Aquest lloc web no està afiliat a ni a cap altre producte esmentat en la comparació. A mesura que evoluciona el panorama de les eines de finances personals, és essencial verificar qualsevol detall o canvi específic directament a la pàgina del producte corresponent. Necessites actualitzar dades? Ajuda’ns a mantenir la informació precisa a GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Tingues en compte que la informació proporcionada a la taula comparativa Ghostfolio vs es basa en la nostra investigació i anàlisi independents. Aquest lloc web no està afiliat a ni a cap altre producte esmentat en la comparació. A mesura que evoluciona el panorama de les eines de finances personals, és essencial verificar qualsevol detall o canvi específic directament a la pàgina del producte corresponent. Necessites actualitzar dades? Ajuda’ns a mantenir la informació precisa a GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -6537,7 +6537,7 @@ Alternativa apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6545,7 +6545,7 @@ Aplicació apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6553,7 +6553,7 @@ Pressupost apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6613,7 +6613,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6629,7 +6629,7 @@ Oficina familiar apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6637,7 +6637,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6649,7 +6649,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6661,7 +6661,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6669,7 +6669,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6677,7 +6677,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6685,7 +6685,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6693,7 +6693,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6701,7 +6701,7 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6709,7 +6709,7 @@ Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6865,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7013,8 +7013,8 @@ - is Open Source Software - is Open Source Software + is Open Source Software + is Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7025,8 +7025,8 @@ - is not Open Source Software - is not Open Source Software + is not Open Source Software + is not Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7061,8 +7061,8 @@ - can be self-hosted - can be self-hosted + can be self-hosted + can be self-hosted apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7081,8 +7081,8 @@ - cannot be self-hosted - cannot be self-hosted + cannot be self-hosted + cannot be self-hosted apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7093,8 +7093,8 @@ - can be used anonymously - can be used anonymously + can be used anonymously + can be used anonymously apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7105,8 +7105,8 @@ - cannot be used anonymously - cannot be used anonymously + cannot be used anonymously + cannot be used anonymously apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7117,8 +7117,8 @@ - offers a free plan - offers a free plan + offers a free plan + offers a free plan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7129,8 +7129,8 @@ - does not offer a free plan - does not offer a free plan + does not offer a free plan + does not offer a free plan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 5ebf979ed..eda5d502d 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -262,7 +262,7 @@ Löschen apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -2230,7 +2230,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -3782,7 +3782,7 @@ Ablauf apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -5495,24 +5495,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Suchst du nach einer Open Source Alternative zu ? Ghostfolio ist ein leistungsstarkes Portfolio Management Tool, das Privatpersonen eine umfassende Plattform bietet, um ihre Investitionen zu verfolgen, zu analysieren und zu optimieren. Egal, ob du ein erfahrener Investor bist oder gerade erst anfängst, Ghostfolio bietet eine intuitive Benutzeroberfläche und eine Vielzahl an Funktionen, die dir dabei helfen, fundierte Entscheidungen zu treffen und die Kontrolle über deine finanzielle Zukunft zu übernehmen. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Suchst du nach einer Open Source Alternative zu ? Ghostfolio ist ein leistungsstarkes Portfolio Management Tool, das Privatpersonen eine umfassende Plattform bietet, um ihre Investitionen zu verfolgen, zu analysieren und zu optimieren. Egal, ob du ein erfahrener Investor bist oder gerade erst anfängst, Ghostfolio bietet eine intuitive Benutzeroberfläche und eine Vielzahl an Funktionen, die dir dabei helfen, fundierte Entscheidungen zu treffen und die Kontrolle über deine finanzielle Zukunft zu übernehmen. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio ist eine Open Source Software (OSS), die eine kostengünstige Alternative zu darstellt und sich besonders für Personen mit knappem Budget eignet, wie z.B. für diejenigen, die finanzielle Unabhängigkeit und einen frühen Ruhestand anstreben (FIRE). Ghostfolio nutzt die gemeinsamen Aktivitäten einer Community von Entwicklern und Finanzenthusiasten, um seine Funktionalität, Sicherheit und Benutzerfreundlichkeit kontinuierlich zu verbessern. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio ist eine Open Source Software (OSS), die eine kostengünstige Alternative zu darstellt und sich besonders für Personen mit knappem Budget eignet, wie z.B. für diejenigen, die finanzielle Unabhängigkeit und einen frühen Ruhestand anstreben (FIRE). Ghostfolio nutzt die gemeinsamen Aktivitäten einer Community von Entwicklern und Finanzenthusiasten, um seine Funktionalität, Sicherheit und Benutzerfreundlichkeit kontinuierlich zu verbessern. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Wir möchten uns in der untenstehenden Ghostfolio vs Vergleichstabelle ein detailliertes Bild davon machen, wie Ghostfolio im Vergleich zu positioniert ist. Wir werden dabei verschiedene Aspekte wie Funktionen, Datenschutz, Preise und weiteres untersuchen, damit du eine gut informierte Entscheidung für deine persönlichen Anforderungen treffen kannst. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Wir möchten uns in der untenstehenden Ghostfolio vs Vergleichstabelle ein detailliertes Bild davon machen, wie Ghostfolio im Vergleich zu positioniert ist. Wir werden dabei verschiedene Aspekte wie Funktionen, Datenschutz, Preise und weiteres untersuchen, damit du eine gut informierte Entscheidung für deine persönlichen Anforderungen treffen kannst. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5532,8 +5532,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Bitte beachte, dass die bereitgestellten Ghostfolio vs Informationen auf unserer unabhängigen Recherche und Analyse beruhen. Diese Webseite steht in keiner Verbindung zu oder einem anderen im Vergleich erwähnten Produkt. Da sich die Landschaft der Personal Finance Tools ständig weiterentwickelt, ist es wichtig, alle spezifischen Details oder Änderungen direkt auf der jeweiligen Produktseite zu überprüfen. Brauchen die Daten eine Auffrischung? Unterstütze uns bei der Pflege der aktuellen Daten auf GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Bitte beachte, dass die bereitgestellten Ghostfolio vs Informationen auf unserer unabhängigen Recherche und Analyse beruhen. Diese Webseite steht in keiner Verbindung zu oder einem anderen im Vergleich erwähnten Produkt. Da sich die Landschaft der Personal Finance Tools ständig weiterentwickelt, ist es wichtig, alle spezifischen Details oder Änderungen direkt auf der jeweiligen Produktseite zu überprüfen. Brauchen die Daten eine Auffrischung? Unterstütze uns bei der Pflege der aktuellen Daten auf GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5552,7 +5552,7 @@ Weltweit apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5856,8 +5856,8 @@ - Ghostfolio vs comparison table - Ghostfolio vs Vergleichstabelle + Ghostfolio vs comparison table + Ghostfolio vs Vergleichstabelle apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -6092,7 +6092,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6561,7 +6561,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6569,7 +6569,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6577,7 +6577,7 @@ Budgetierung apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6637,7 +6637,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6653,7 +6653,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6661,7 +6661,7 @@ Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6673,7 +6673,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6685,7 +6685,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6693,7 +6693,7 @@ Datenschutz apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6701,7 +6701,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6709,7 +6709,7 @@ Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6717,7 +6717,7 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6725,7 +6725,7 @@ Vermögen apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6733,7 +6733,7 @@ Vermögensverwaltung apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6889,7 +6889,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7037,8 +7037,8 @@ - is Open Source Software - ist Open Source Software + is Open Source Software + ist Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7049,8 +7049,8 @@ - is not Open Source Software - ist keine Open Source Software + is not Open Source Software + ist keine Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7085,8 +7085,8 @@ - can be self-hosted - kann selbst gehostet werden + can be self-hosted + kann selbst gehostet werden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7105,8 +7105,8 @@ - cannot be self-hosted - kann nicht selbst gehostet werden + cannot be self-hosted + kann nicht selbst gehostet werden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7117,8 +7117,8 @@ - can be used anonymously - kann anonym genutzt werden + can be used anonymously + kann anonym genutzt werden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7129,8 +7129,8 @@ - cannot be used anonymously - kann nicht anonym genutzt werden + cannot be used anonymously + kann nicht anonym genutzt werden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7141,8 +7141,8 @@ - offers a free plan - hat ein kostenloses Angebot + offers a free plan + hat ein kostenloses Angebot apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7153,8 +7153,8 @@ - does not offer a free plan - hat kein kostenloses Angebot + does not offer a free plan + hat kein kostenloses Angebot apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index 1b7dc66b7..a0ed117fa 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -263,7 +263,7 @@ Eliminar apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -2215,7 +2215,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -3767,7 +3767,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -5472,24 +5472,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - ¿Estás buscando una alternativa de código abierto a ? Ghostfolio es una potente herramienta de gestión de carteras que proporciona a los usuarios una plataforma completa para seguir, analizar y optimizar sus inversiones. Ya seas un inversor con experiencia o estés comenzando, Ghostfolio ofrece una interfaz intuitiva y una amplia gama de funcionalidades para ayudarte a tomar decisiones informadas y tener el control de tu futuro financiero. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + ¿Estás buscando una alternativa de código abierto a ? Ghostfolio es una potente herramienta de gestión de carteras que proporciona a los usuarios una plataforma completa para seguir, analizar y optimizar sus inversiones. Ya seas un inversor con experiencia o estés comenzando, Ghostfolio ofrece una interfaz intuitiva y una amplia gama de funcionalidades para ayudarte a tomar decisiones informadas y tener el control de tu futuro financiero. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio es un software de código abierto (OSS), que ofrece una alternativa rentable a lo que lo hace especialmente adecuado para personas con un presupuesto ajustado, como aquellas que buscan la Independencia Financiera y Jubilación Anticipada (FIRE). Al aprovechar los esfuerzos colectivos de una comunidad de desarrolladores y entusiastas de las finanzas personales, Ghostfolio mejora continuamente sus capacidades, seguridad y experiencia de usuario. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio es un software de código abierto (OSS), que ofrece una alternativa rentable a lo que lo hace especialmente adecuado para personas con un presupuesto ajustado, como aquellas que buscan la Independencia Financiera y Jubilación Anticipada (FIRE). Al aprovechar los esfuerzos colectivos de una comunidad de desarrolladores y entusiastas de las finanzas personales, Ghostfolio mejora continuamente sus capacidades, seguridad y experiencia de usuario. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Analicemos en detalle la tabla comparativa entre Ghostfolio y que encontrarás a continuación, para obtener una comprensión completa de cómo se posiciona Ghostfolio en relación con . Exploraremos diversos aspectos como funcionalidades, privacidad de los datos, precios y más, lo que te permitirá tomar una decisión bien fundamentada según tus necesidades personales. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Analicemos en detalle la tabla comparativa entre Ghostfolio y que encontrarás a continuación, para obtener una comprensión completa de cómo se posiciona Ghostfolio en relación con . Exploraremos diversos aspectos como funcionalidades, privacidad de los datos, precios y más, lo que te permitirá tomar una decisión bien fundamentada según tus necesidades personales. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5509,8 +5509,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Ten en cuenta que la información proporcionada en la tabla comparativa entre Ghostfolio y se basa en nuestra investigación y análisis independientes. Este sitio web no está afiliado con ni con ningún otro producto mencionado en la comparación. Dado que el panorama de las herramientas de finanzas personales evoluciona constantemente, es fundamental verificar cualquier detalle específico o cambio directamente en la página oficial del producto correspondiente. ¿Los datos necesitan una actualización? Ayúdanos a mantener la información precisa en GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Ten en cuenta que la información proporcionada en la tabla comparativa entre Ghostfolio y se basa en nuestra investigación y análisis independientes. Este sitio web no está afiliado con ni con ningún otro producto mencionado en la comparación. Dado que el panorama de las herramientas de finanzas personales evoluciona constantemente, es fundamental verificar cualquier detalle específico o cambio directamente en la página oficial del producto correspondiente. ¿Los datos necesitan una actualización? Ayúdanos a mantener la información precisa en GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5529,7 +5529,7 @@ Global apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5833,8 +5833,8 @@ - Ghostfolio vs comparison table - Tabla comparativa de Ghostfolio vs + Ghostfolio vs comparison table + Tabla comparativa de Ghostfolio vs apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -6069,7 +6069,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6538,7 +6538,7 @@ Alternativa apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6546,7 +6546,7 @@ Aplicación apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6554,7 +6554,7 @@ Presupuestos apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6614,7 +6614,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6630,7 +6630,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6638,7 +6638,7 @@ Inversor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6650,7 +6650,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6662,7 +6662,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6670,7 +6670,7 @@ Privacidad apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6678,7 +6678,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6686,7 +6686,7 @@ Herramienta apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6694,7 +6694,7 @@ Experiencia del usuario apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6702,7 +6702,7 @@ Patrimonio apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6710,7 +6710,7 @@ Gestión de patrimonios apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6866,7 +6866,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7014,8 +7014,8 @@ - is Open Source Software - es software de código abierto + is Open Source Software + es software de código abierto apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7026,8 +7026,8 @@ - is not Open Source Software - no es software de código abierto + is not Open Source Software + no es software de código abierto apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7062,8 +7062,8 @@ - can be self-hosted - se puede autoalojar + can be self-hosted + se puede autoalojar apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7082,8 +7082,8 @@ - cannot be self-hosted - no se puede autoalojar + cannot be self-hosted + no se puede autoalojar apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7094,8 +7094,8 @@ - can be used anonymously - se puede usar de forma anónima + can be used anonymously + se puede usar de forma anónima apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7106,8 +7106,8 @@ - cannot be used anonymously - no se puede usar de forma anónima + cannot be used anonymously + no se puede usar de forma anónima apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7118,8 +7118,8 @@ - offers a free plan - ofrece un plan gratuito + offers a free plan + ofrece un plan gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7130,8 +7130,8 @@ - does not offer a free plan - no ofrece un plan gratuito + does not offer a free plan + no ofrece un plan gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index d4d2d15e4..ef96b4a18 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -318,7 +318,7 @@ Supprimer apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -954,7 +954,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -3766,7 +3766,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -5471,24 +5471,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Cherchez-vous des alternatives open source à ? Ghostfolio est un outil de gestion de portefeuille puissant offrant aux particuliers une plateforme complète pour suivre, analyser et optimiser ses investissements. Que vous soyez un investisseur expérimenté ou que vous débutiez, Ghostfolio offre une interface utilisateur intuitive et une large gamme de fonctionnalités pour vous aider à prendre des décisions éclairées et à prendre le contrôle de votre avenir financier. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Cherchez-vous des alternatives open source à ? Ghostfolio est un outil de gestion de portefeuille puissant offrant aux particuliers une plateforme complète pour suivre, analyser et optimiser ses investissements. Que vous soyez un investisseur expérimenté ou que vous débutiez, Ghostfolio offre une interface utilisateur intuitive et une large gamme de fonctionnalités pour vous aider à prendre des décisions éclairées et à prendre le contrôle de votre avenir financier. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio est un logiciel open source (OSS), offrant une alternative économique, le rendant particulièrement adaptée aux personnes disposant d’un budget serré, telles que celles qui cherchent à atteindre le mouvement Financial Independence, Retire Early (FIRE). En s’appuyant sur les efforts collectifs d’une communauté de développeurs et de passionnés de finances personnelles, Ghostfolio améliore continuellement ses fonctionnalités, sa sécurité et son expérience utilisateur. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio est un logiciel open source (OSS), offrant une alternative économique, le rendant particulièrement adaptée aux personnes disposant d’un budget serré, telles que celles qui cherchent à atteindre le mouvement Financial Independence, Retire Early (FIRE). En s’appuyant sur les efforts collectifs d’une communauté de développeurs et de passionnés de finances personnelles, Ghostfolio améliore continuellement ses fonctionnalités, sa sécurité et son expérience utilisateur. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Regardons plus en détails ce que proposent Ghostfolio vs via la table de comparaison ci-dessous pour comprendre comment Ghostfolio se positionne par rapport à . Nous examinerons divers aspects tels que les fonctionnalités, la confidentialité des données, le prix, etc., afin de vous permettre de faire un choix éclairé en fonction de vos besoins personnels. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Regardons plus en détails ce que proposent Ghostfolio vs via la table de comparaison ci-dessous pour comprendre comment Ghostfolio se positionne par rapport à . Nous examinerons divers aspects tels que les fonctionnalités, la confidentialité des données, le prix, etc., afin de vous permettre de faire un choix éclairé en fonction de vos besoins personnels. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5508,8 +5508,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Veuillez noter que les informations fournies dans le Ghostfolio vs via la table de comparaison se basent de nos recherches et analyses indépendantes. Ce site n’est pas affilié à ou tout autre produit mentionné dans la comparaison. Le paysage des outils de finances personnelles évoluant, il est essentiel de vérifier tout détail ou changement spécifique directement sur la page du produit concerné. Certaines données doivent être mises à jour ? Aidez-nous à maintenir les données exactes sur GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Veuillez noter que les informations fournies dans le Ghostfolio vs via la table de comparaison se basent de nos recherches et analyses indépendantes. Ce site n’est pas affilié à ou tout autre produit mentionné dans la comparaison. Le paysage des outils de finances personnelles évoluant, il est essentiel de vérifier tout détail ou changement spécifique directement sur la page du produit concerné. Certaines données doivent être mises à jour ? Aidez-nous à maintenir les données exactes sur GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5528,7 +5528,7 @@ Mondial apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5832,8 +5832,8 @@ - Ghostfolio vs comparison table - Ghostfolio vs tableau comparatif + Ghostfolio vs comparison table + Ghostfolio vs tableau comparatif apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -6068,7 +6068,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6537,7 +6537,7 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6545,7 +6545,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6553,7 +6553,7 @@ Budget apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6613,7 +6613,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6629,7 +6629,7 @@ Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6637,7 +6637,7 @@ Investisseur apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6649,7 +6649,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6661,7 +6661,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6669,7 +6669,7 @@ Confidentialité apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6677,7 +6677,7 @@ Logiciels apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6685,7 +6685,7 @@ Outils apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6693,7 +6693,7 @@ Expérience Utilisateur apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6701,7 +6701,7 @@ Patrimoine apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6709,7 +6709,7 @@ Gestion de Patrimoine apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6865,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7013,8 +7013,8 @@ - is Open Source Software - est un logiciel open source + is Open Source Software + est un logiciel open source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7025,8 +7025,8 @@ - is not Open Source Software - n’est pas un logiciel open source + is not Open Source Software + n’est pas un logiciel open source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7061,8 +7061,8 @@ - can be self-hosted - peut être auto-hébergé + can be self-hosted + peut être auto-hébergé apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7081,8 +7081,8 @@ - cannot be self-hosted - ne peut pas être auto-hébergé + cannot be self-hosted + ne peut pas être auto-hébergé apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7093,8 +7093,8 @@ - can be used anonymously - peut être utilisé de manière anonyme + can be used anonymously + peut être utilisé de manière anonyme apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7105,8 +7105,8 @@ - cannot be used anonymously - ne peut pas être utilisé de manière anonyme + cannot be used anonymously + ne peut pas être utilisé de manière anonyme apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7117,8 +7117,8 @@ - offers a free plan - propose un plan gratuit + offers a free plan + propose un plan gratuit apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7129,8 +7129,8 @@ - does not offer a free plan - ne propose pas de plan gratuit + does not offer a free plan + ne propose pas de plan gratuit apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index ffc753de9..e009676df 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -263,7 +263,7 @@ Elimina apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -2215,7 +2215,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -3767,7 +3767,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -5472,24 +5472,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Stai cercando un’alternativa open source a ? Ghostfolio è un potente strumento di gestione del portafoglio che fornisce alle persone una piattaforma completa per monitorare, analizzare e ottimizzare i propri investimenti. Che tu sia un investitore esperto o alle prime armi, Ghostfolio offre un’interfaccia utente intuitiva e un’ampia gamma di funzionalità per aiutarti a prendere decisioni informate e il controllo del tuo futuro finanziario. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Stai cercando un’alternativa open source a ? Ghostfolio è un potente strumento di gestione del portafoglio che fornisce alle persone una piattaforma completa per monitorare, analizzare e ottimizzare i propri investimenti. Che tu sia un investitore esperto o alle prime armi, Ghostfolio offre un’interfaccia utente intuitiva e un’ampia gamma di funzionalità per aiutarti a prendere decisioni informate e il controllo del tuo futuro finanziario. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio è un software open source (OSS) che offre un’alternativa economicamente vantaggiosa a particolarmente adatta a persone con un budget limitato, come quelle che perseguono l’indipendenza finanziaria e il pensionamento anticipato (FIRE). Grazie agli sforzi collettivi di una comunità di sviluppatori e di appassionati di finanza personale, Ghostfolio migliora continuamente le sue capacità, la sua sicurezza e la sua esperienza utente. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio è un software open source (OSS) che offre un’alternativa economicamente vantaggiosa a particolarmente adatta a persone con un budget limitato, come quelle che perseguono l’indipendenza finanziaria e il pensionamento anticipato (FIRE). Grazie agli sforzi collettivi di una comunità di sviluppatori e di appassionati di finanza personale, Ghostfolio migliora continuamente le sue capacità, la sua sicurezza e la sua esperienza utente. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Analizziamo nel dettaglio la tabella di confronto qui sotto per comprendere a fondo come Ghostfolio si posiziona rispetto a . Esploreremo vari aspetti come le caratteristiche, la privacy dei dati, il prezzo e altro ancora, permettendoti di fare una scelta ben informata per le tue esigenze personali. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Analizziamo nel dettaglio la tabella di confronto qui sotto per comprendere a fondo come Ghostfolio si posiziona rispetto a . Esploreremo vari aspetti come le caratteristiche, la privacy dei dati, il prezzo e altro ancora, permettendoti di fare una scelta ben informata per le tue esigenze personali. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5509,8 +5509,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Nota bene: le informazioni fornite si basano sulle nostre ricerche e analisi indipendenti. Questo sito web non è affiliato con o a qualsiasi altro prodotto citato nel confronto. Poiché il panorama degli strumenti di finanza personale si evolve, è essenziale verificare qualsiasi dettaglio o modifica specifica direttamente nella pagina del prodotto in questione. I dati hanno bisogno di essere aggiornati? Aiutaci a mantenere i dati accurati su GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Nota bene: le informazioni fornite si basano sulle nostre ricerche e analisi indipendenti. Questo sito web non è affiliato con o a qualsiasi altro prodotto citato nel confronto. Poiché il panorama degli strumenti di finanza personale si evolve, è essenziale verificare qualsiasi dettaglio o modifica specifica direttamente nella pagina del prodotto in questione. I dati hanno bisogno di essere aggiornati? Aiutaci a mantenere i dati accurati su GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5529,7 +5529,7 @@ Globale apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5833,8 +5833,8 @@ - Ghostfolio vs comparison table - Ghostfolio vs tabella di comparazione + Ghostfolio vs comparison table + Ghostfolio vs tabella di comparazione apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -6069,7 +6069,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6538,7 +6538,7 @@ Alternativa apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6546,7 +6546,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6554,7 +6554,7 @@ Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6614,7 +6614,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6630,7 +6630,7 @@ Ufficio familiare apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6638,7 +6638,7 @@ Investitore apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6650,7 +6650,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6662,7 +6662,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6670,7 +6670,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6678,7 +6678,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6686,7 +6686,7 @@ Strumento apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6694,7 +6694,7 @@ Esperienza Utente apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6702,7 +6702,7 @@ Ricchezza apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6710,7 +6710,7 @@ Gestione Patrimoniale apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6866,7 +6866,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7014,8 +7014,8 @@ - is Open Source Software - è un programma Open Source + is Open Source Software + è un programma Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7026,8 +7026,8 @@ - is not Open Source Software - non è un programma Open Source + is not Open Source Software + non è un programma Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7062,8 +7062,8 @@ - can be self-hosted - può essere ospitato in proprio + can be self-hosted + può essere ospitato in proprio apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7082,8 +7082,8 @@ - cannot be self-hosted - non può essere ospitato in proprio + cannot be self-hosted + non può essere ospitato in proprio apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7094,8 +7094,8 @@ - can be used anonymously - può essere usato anonimamente + can be used anonymously + può essere usato anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7106,8 +7106,8 @@ - cannot be used anonymously - non può essere usato anonimamente + cannot be used anonymously + non può essere usato anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7118,8 +7118,8 @@ - offers a free plan - ha un piano gratuito + offers a free plan + ha un piano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7130,8 +7130,8 @@ - does not offer a free plan - non ha un piano gratuito + does not offer a free plan + non ha un piano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.ko.xlf b/apps/client/src/locales/messages.ko.xlf index 29e35fbb7..f3e4cea1a 100644 --- a/apps/client/src/locales/messages.ko.xlf +++ b/apps/client/src/locales/messages.ko.xlf @@ -548,7 +548,7 @@ 삭제 apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -1532,7 +1532,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -4608,7 +4608,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -4793,24 +4793,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - 에 대한 오픈소스 대안을 찾고 계십니까? Ghostfolio는 개인에게 투자를 추적, 분석, 최적화할 수 있는 포괄적인 플랫폼을 제공하는 강력한 포트폴리오 관리 도구입니다. 숙련된 투자자이든 이제 막 시작한 투자자이든 Ghostfolio는 직관적인 사용자 인터페이스와 다양한 기능을 제공하여 정보에 입각한 결정을 내리고 재정적 미래를 관리하는 데 도움을 줍니다. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + 에 대한 오픈소스 대안을 찾고 계십니까? Ghostfolio는 개인에게 투자를 추적, 분석, 최적화할 수 있는 포괄적인 플랫폼을 제공하는 강력한 포트폴리오 관리 도구입니다. 숙련된 투자자이든 이제 막 시작한 투자자이든 Ghostfolio는 직관적인 사용자 인터페이스와 다양한 기능을 제공하여 정보에 입각한 결정을 내리고 재정적 미래를 관리하는 데 도움을 줍니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio는 오픈 소스 소프트웨어로, 에 대한 비용 효율적인 대안을 제공하므로 재정적 독립, 조기 퇴직(FIRE)을 추구하는 사람과 같이 예산이 부족한 개인에게 특히 적합합니다. Ghostfolio는 개발자 커뮤니티와 개인 금융 애호가들의 공동 노력을 활용하여 기능, 보안 및 사용자 경험을 지속적으로 향상시킵니다. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio는 오픈 소스 소프트웨어로, 에 대한 비용 효율적인 대안을 제공하므로 재정적 독립, 조기 퇴직(FIRE)을 추구하는 사람과 같이 예산이 부족한 개인에게 특히 적합합니다. Ghostfolio는 개발자 커뮤니티와 개인 금융 애호가들의 공동 노력을 활용하여 기능, 보안 및 사용자 경험을 지속적으로 향상시킵니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Ghostfolio가 과 관련하여 어떻게 위치하는지 철저하게 이해하기 위해 아래의 자세한 Ghostfolio 대 비교표를 자세히 살펴보겠습니다. 우리는 기능, 데이터 개인 정보 보호, 가격 등과 같은 다양한 측면을 탐색하여 귀하의 개인 요구 사항에 맞는 정보를 바탕으로 선택할 수 있도록 할 것입니다. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Ghostfolio가 과 관련하여 어떻게 위치하는지 철저하게 이해하기 위해 아래의 자세한 Ghostfolio 대 비교표를 자세히 살펴보겠습니다. 우리는 기능, 데이터 개인 정보 보호, 가격 등과 같은 다양한 측면을 탐색하여 귀하의 개인 요구 사항에 맞는 정보를 바탕으로 선택할 수 있도록 할 것입니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4829,8 +4829,8 @@ - Ghostfolio vs comparison table - Ghostfolio와 비교표 + Ghostfolio vs comparison table + Ghostfolio와 비교표 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -4993,8 +4993,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Ghostfolio와 비교표에 제공된 정보는 당사의 독립적인 연구 및 분석을 기반으로 한 것입니다. 이 웹사이트는 또는 비교에 언급된 다른 제품과 관련이 없습니다. 개인 금융 도구의 환경이 발전함에 따라 각 제품 페이지에서 직접 특정 세부 정보나 변경 사항을 확인하는 것이 중요합니다. 데이터를 새로 고쳐야 합니까? 깃허브에서 정확한 데이터를 유지할 수 있도록 도와주세요. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Ghostfolio와 비교표에 제공된 정보는 당사의 독립적인 연구 및 분석을 기반으로 한 것입니다. 이 웹사이트는 또는 비교에 언급된 다른 제품과 관련이 없습니다. 개인 금융 도구의 환경이 발전함에 따라 각 제품 페이지에서 직접 특정 세부 정보나 변경 사항을 확인하는 것이 중요합니다. 데이터를 새로 고쳐야 합니까? 깃허브에서 정확한 데이터를 유지할 수 있도록 도와주세요. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5021,7 +5021,7 @@ 글로벌 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -6117,7 +6117,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6562,7 +6562,7 @@ 재산 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6622,7 +6622,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6638,7 +6638,7 @@ 사용자 경험 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6646,7 +6646,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6654,7 +6654,7 @@ 도구 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6662,7 +6662,7 @@ 투자자 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6670,7 +6670,7 @@ 자산관리 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6686,7 +6686,7 @@ 대안 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6694,7 +6694,7 @@ 패밀리오피스 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6706,7 +6706,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6714,7 +6714,7 @@ 소프트웨어 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6722,7 +6722,7 @@ 예산 편성 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6734,7 +6734,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6742,7 +6742,7 @@ 은둔 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6882,7 +6882,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7022,8 +7022,8 @@ - offers a free plan - 은(는) 무료 요금제를 제공합니다 + offers a free plan + 은(는) 무료 요금제를 제공합니다 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7034,8 +7034,8 @@ - does not offer a free plan - 은(는) 무료 요금제를 제공하지 않습니다. + does not offer a free plan + 은(는) 무료 요금제를 제공하지 않습니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 @@ -7078,8 +7078,8 @@ - can be self-hosted - 은(는) 자체 호스팅 가능 + can be self-hosted + 은(는) 자체 호스팅 가능 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7098,8 +7098,8 @@ - cannot be self-hosted - 은(는) 자체 호스팅할 수 없습니다. + cannot be self-hosted + 은(는) 자체 호스팅할 수 없습니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7110,8 +7110,8 @@ - can be used anonymously - 은(는) 익명으로 사용할 수 있습니다. + can be used anonymously + 은(는) 익명으로 사용할 수 있습니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7122,8 +7122,8 @@ - cannot be used anonymously - 은(는) 익명으로 사용할 수 없습니다. + cannot be used anonymously + 은(는) 익명으로 사용할 수 없습니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7134,8 +7134,8 @@ - is not Open Source Software - 은(는) 오픈 소스 소프트웨어가 아닙니다. + is not Open Source Software + 은(는) 오픈 소스 소프트웨어가 아닙니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7146,8 +7146,8 @@ - is Open Source Software - 은(는) 오픈 소스 소프트웨어입니다. + is Open Source Software + 은(는) 오픈 소스 소프트웨어입니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index fea2393fb..326134729 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -262,7 +262,7 @@ Verwijderen apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -2214,7 +2214,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -3766,7 +3766,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -5471,24 +5471,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Ben je op zoek naar een open source alternatief voor ? Ghostfolio is een krachtige tool voor portefeuillebeheer die particulieren een uitgebreid platform biedt om hun beleggingen bij te houden, te analyseren en te optimaliseren. Of je nu een ervaren belegger bent of net begint, Ghostfolio biedt een intuïtieve gebruikersinterface en uitgebreide functionaliteiten om je te helpen weloverwogen beslissingen te nemen en je financiële toekomst in eigen handen te nemen. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Ben je op zoek naar een open source alternatief voor ? Ghostfolio is een krachtige tool voor portefeuillebeheer die particulieren een uitgebreid platform biedt om hun beleggingen bij te houden, te analyseren en te optimaliseren. Of je nu een ervaren belegger bent of net begint, Ghostfolio biedt een intuïtieve gebruikersinterface en uitgebreide functionaliteiten om je te helpen weloverwogen beslissingen te nemen en je financiële toekomst in eigen handen te nemen. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio is open source software (OSS) en biedt een kosteneffectief alternatief voor waardoor het bijzonder geschikt is voor mensen met een krap budget, zoals degenen Financiële onafhankelijkheid nastreven, vroeg met pensioen gaan (FIRE). Door gebruik te maken van de collectieve inspanningen van een gemeenschap van ontwikkelaars en liefhebbers van persoonlijke financiën, verbetert Ghostfolio voortdurend de mogelijkheden, veiligheid en gebruikerservaring. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio is open source software (OSS) en biedt een kosteneffectief alternatief voor waardoor het bijzonder geschikt is voor mensen met een krap budget, zoals degenen Financiële onafhankelijkheid nastreven, vroeg met pensioen gaan (FIRE). Door gebruik te maken van de collectieve inspanningen van een gemeenschap van ontwikkelaars en liefhebbers van persoonlijke financiën, verbetert Ghostfolio voortdurend de mogelijkheden, veiligheid en gebruikerservaring. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Laten we eens dieper duiken in de gedetailleerde vergelijkingstabel hieronder om een beter begrip te krijgen hoe Ghostfolio zichzelf positioneert ten opzichte van . We gaan in op verschillende aspecten zoals functies, gegevensprivacy, prijzen en meer, zodat je een weloverwogen keuze kunt maken voor jouw persoonlijke behoeften. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Laten we eens dieper duiken in de gedetailleerde vergelijkingstabel hieronder om een beter begrip te krijgen hoe Ghostfolio zichzelf positioneert ten opzichte van . We gaan in op verschillende aspecten zoals functies, gegevensprivacy, prijzen en meer, zodat je een weloverwogen keuze kunt maken voor jouw persoonlijke behoeften. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5508,8 +5508,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Houd er rekening mee dat de verstrekte informatie in deze Ghostfolio vs is gebaseerd op ons onafhankelijk onderzoek en analyse. Deze website is niet gelieerd aan of een ander product dat in de vergelijking wordt genoemd. Aangezien het landschap van tools voor persoonlijke financiën evolueert, is het essentieel om specifieke details of wijzigingen rechtstreeks op de betreffende productpagina te controleren. Hebben je gegevens een opfrisbeurt nodig? Help ons de gegevens nauwkeurig te houden op GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Houd er rekening mee dat de verstrekte informatie in deze Ghostfolio vs is gebaseerd op ons onafhankelijk onderzoek en analyse. Deze website is niet gelieerd aan of een ander product dat in de vergelijking wordt genoemd. Aangezien het landschap van tools voor persoonlijke financiën evolueert, is het essentieel om specifieke details of wijzigingen rechtstreeks op de betreffende productpagina te controleren. Hebben je gegevens een opfrisbeurt nodig? Help ons de gegevens nauwkeurig te houden op GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5528,7 +5528,7 @@ Wereldwijd apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5832,8 +5832,8 @@ - Ghostfolio vs comparison table - Ghostfolio vs vergelijkingstabel + Ghostfolio vs comparison table + Ghostfolio vs vergelijkingstabel apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -6068,7 +6068,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6537,7 +6537,7 @@ Alternatief apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6545,7 +6545,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6553,7 +6553,7 @@ Budgetteren apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6613,7 +6613,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6629,7 +6629,7 @@ Familiekantoor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6637,7 +6637,7 @@ Investeerder apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6649,7 +6649,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6661,7 +6661,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6669,7 +6669,7 @@ Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6677,7 +6677,7 @@ Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6685,7 +6685,7 @@ Hulpmiddel apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6693,7 +6693,7 @@ Gebruikers Ervaring apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6701,7 +6701,7 @@ Vermogen apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6709,7 +6709,7 @@ Vermogensbeheer apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6865,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7013,8 +7013,8 @@ - is Open Source Software - is Open Source Software + is Open Source Software + is Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7025,8 +7025,8 @@ - is not Open Source Software - is geen Open Source Software + is not Open Source Software + is geen Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7061,8 +7061,8 @@ - can be self-hosted - kan zelf gehost worden + can be self-hosted + kan zelf gehost worden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7081,8 +7081,8 @@ - cannot be self-hosted - kan niet zelf gehost worden + cannot be self-hosted + kan niet zelf gehost worden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7093,8 +7093,8 @@ - can be used anonymously - kan anoniem gebruikt worden + can be used anonymously + kan anoniem gebruikt worden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7105,8 +7105,8 @@ - cannot be used anonymously - kan niet anoniem gebruik worden + cannot be used anonymously + kan niet anoniem gebruik worden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7117,8 +7117,8 @@ - offers a free plan - biedt een gratis abonnement aan + offers a free plan + biedt een gratis abonnement aan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7129,8 +7129,8 @@ - does not offer a free plan - biedt geen gratis abonnement aan + does not offer a free plan + biedt geen gratis abonnement aan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index 2fcbca736..385c1fad4 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -539,7 +539,7 @@ Usuń apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -1499,7 +1499,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -4575,7 +4575,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -4760,24 +4760,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Szukasz alternatywy typu open source dla ? Ghostfolio to potężne narzędzie do zarządzania portfelem, które zapewnia osobom fizycznym kompleksową platformę do śledzenia, analizowania i optymalizacji ich inwestycji. Niezależnie od tego, czy jesteś doświadczonym inwestorem, czy dopiero zaczynasz, Ghostfolio oferuje intuicyjny interfejs użytkownika i szeroki zakres funkcjonalności, które pomogą Ci podejmować przemyślane decyzje i przejąć kontrolę nad swoją finansową przyszłością. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Szukasz alternatywy typu open source dla ? Ghostfolio to potężne narzędzie do zarządzania portfelem, które zapewnia osobom fizycznym kompleksową platformę do śledzenia, analizowania i optymalizacji ich inwestycji. Niezależnie od tego, czy jesteś doświadczonym inwestorem, czy dopiero zaczynasz, Ghostfolio oferuje intuicyjny interfejs użytkownika i szeroki zakres funkcjonalności, które pomogą Ci podejmować przemyślane decyzje i przejąć kontrolę nad swoją finansową przyszłością. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio to oprogramowanie o otwartym kodzie źródłowym (OSS), stanowiące opłacalną alternatywę dla , czyniąc go szczególnie odpowiednim dla osób o ograniczonym budżecie, takich jak osoby dążące do finansowej niezależności i wcześniejszej emerytury (Financial Independence, Retire Early - FIRE). Wykorzystując wspólne wysiłki społeczności programistów i entuzjastów finansów osobistych, Ghostfolio stale zwiększa swoje możliwości, bezpieczeństwo i komfort użytkowania. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio to oprogramowanie o otwartym kodzie źródłowym (OSS), stanowiące opłacalną alternatywę dla , czyniąc go szczególnie odpowiednim dla osób o ograniczonym budżecie, takich jak osoby dążące do finansowej niezależności i wcześniejszej emerytury (Financial Independence, Retire Early - FIRE). Wykorzystując wspólne wysiłki społeczności programistów i entuzjastów finansów osobistych, Ghostfolio stale zwiększa swoje możliwości, bezpieczeństwo i komfort użytkowania. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Zagłębmy się w szczegółową tabelę porównawczą Ghostfolio vs poniżej, aby dokładnie zrozumieć, jak Ghostfolio pozycjonuje się w stosunku do . Przeanalizujemy różne aspekty, takie jak funkcje, prywatność danych, opłaty i inne, umożliwiając Tobie dokonanie przemyślanego wyboru pod kątem osobistych wymagań. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Zagłębmy się w szczegółową tabelę porównawczą Ghostfolio vs poniżej, aby dokładnie zrozumieć, jak Ghostfolio pozycjonuje się w stosunku do . Przeanalizujemy różne aspekty, takie jak funkcje, prywatność danych, opłaty i inne, umożliwiając Tobie dokonanie przemyślanego wyboru pod kątem osobistych wymagań. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4796,8 +4796,8 @@ - Ghostfolio vs comparison table - Ghostfolio vs - tabela porównawcza + Ghostfolio vs comparison table + Ghostfolio vs - tabela porównawcza apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -4948,8 +4948,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Należy pamiętać, że informacje zawarte w tabeli porównawczej Ghostfolio vs są oparte na naszych niezależnych badaniach i analizach. Ta strona internetowa nie jest powiązana z ani żadnym innym produktem wymienionym w porównaniu. Ponieważ krajobraz narzędzi do finansów osobistych ewoluuje, ważne jest, aby weryfikować wszelkie szczegóły lub zmiany bezpośrednio na stronie danego produktu. Informacje wymagają aktualizacji? Pomóż nam utrzymywać dokładne dane na GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Należy pamiętać, że informacje zawarte w tabeli porównawczej Ghostfolio vs są oparte na naszych niezależnych badaniach i analizach. Ta strona internetowa nie jest powiązana z ani żadnym innym produktem wymienionym w porównaniu. Ponieważ krajobraz narzędzi do finansów osobistych ewoluuje, ważne jest, aby weryfikować wszelkie szczegóły lub zmiany bezpośrednio na stronie danego produktu. Informacje wymagają aktualizacji? Pomóż nam utrzymywać dokładne dane na GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -4976,7 +4976,7 @@ Globalny apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -6068,7 +6068,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6537,7 +6537,7 @@ Alternatywa apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6545,7 +6545,7 @@ Aplikacja apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6553,7 +6553,7 @@ Budżetowanie apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6613,7 +6613,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6629,7 +6629,7 @@ Biuro Rodzinne apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6637,7 +6637,7 @@ Inwestor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6649,7 +6649,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6661,7 +6661,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6669,7 +6669,7 @@ Prywatność apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6677,7 +6677,7 @@ Oprogramowanie apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6685,7 +6685,7 @@ Narzędzie apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6693,7 +6693,7 @@ Doświadczenie Użytkownika apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6701,7 +6701,7 @@ Majątek apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6709,7 +6709,7 @@ Zarządzanie Majątkiem apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6865,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7013,8 +7013,8 @@ - is Open Source Software - jest Oprogramowaniem Open Source + is Open Source Software + jest Oprogramowaniem Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7025,8 +7025,8 @@ - is not Open Source Software - nie jest Oprogramowaniem Open Source + is not Open Source Software + nie jest Oprogramowaniem Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7061,8 +7061,8 @@ - can be self-hosted - może być hostowana samodzielnie + can be self-hosted + może być hostowana samodzielnie apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7081,8 +7081,8 @@ - cannot be self-hosted - nie może być hostowany samodzielnie + cannot be self-hosted + nie może być hostowany samodzielnie apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7093,8 +7093,8 @@ - can be used anonymously - może być używana anonimowo + can be used anonymously + może być używana anonimowo apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7105,8 +7105,8 @@ - cannot be used anonymously - nie może być używana anonimowo + cannot be used anonymously + nie może być używana anonimowo apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7117,8 +7117,8 @@ - offers a free plan - oferuje darmowy plan + offers a free plan + oferuje darmowy plan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7129,8 +7129,8 @@ - does not offer a free plan - nie oferuje darmowego planu + does not offer a free plan + nie oferuje darmowego planu apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index e7baad543..bfcfdf7a5 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -318,7 +318,7 @@ Eliminar apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -814,7 +814,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -3766,7 +3766,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -5471,24 +5471,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Você está procurando uma alternativa de código aberto para ? Ghostfolio é uma poderosa ferramenta de gestão de portfólio que oferece aos investidores uma plataforma abrangente para monitorar, analisar e otimizar seus investimentos. Seja você um investidor experiente ou iniciante, o Ghostfolio oferece uma interface de usuário intuitiva e um ampla gama de funcionalidades para ajudá-lo a tomar decisões informadas e assumir o controle do seu futuro financeiro. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Você está procurando uma alternativa de código aberto para ? Ghostfolio é uma poderosa ferramenta de gestão de portfólio que oferece aos investidores uma plataforma abrangente para monitorar, analisar e otimizar seus investimentos. Seja você um investidor experiente ou iniciante, o Ghostfolio oferece uma interface de usuário intuitiva e um ampla gama de funcionalidades para ajudá-lo a tomar decisões informadas e assumir o controle do seu futuro financeiro. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio é um software de código aberto (OSS), que oferece uma alternativa econômica para tornando-o particularmente adequado para indivíduos com orçamento apertado, como aqueles buscando Independência Financeira, Aposentadoria Antecipada (FIRE). Ao aproveitar os esforços coletivos de uma comunidade de desenvolvedores e entusiastas de finanças pessoais, o Ghostfolio aprimora continuamente seus recursos, segurança e experiência do usuário. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio é um software de código aberto (OSS), que oferece uma alternativa econômica para tornando-o particularmente adequado para indivíduos com orçamento apertado, como aqueles buscando Independência Financeira, Aposentadoria Antecipada (FIRE). Ao aproveitar os esforços coletivos de uma comunidade de desenvolvedores e entusiastas de finanças pessoais, o Ghostfolio aprimora continuamente seus recursos, segurança e experiência do usuário. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Vamos nos aprofundar nos detalhes do Ghostfolio vs tabela de comparação abaixo para obter uma compreensão completa de como o Ghostfolio se posiciona em relação a . Exploraremos vários aspectos, como recursos, privacidade de dados, preços e muito mais, permitindo que você faça uma escolha bem informada para suas necessidades pessoais. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Vamos nos aprofundar nos detalhes do Ghostfolio vs tabela de comparação abaixo para obter uma compreensão completa de como o Ghostfolio se posiciona em relação a . Exploraremos vários aspectos, como recursos, privacidade de dados, preços e muito mais, permitindo que você faça uma escolha bem informada para suas necessidades pessoais. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5508,8 +5508,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Observe que as informações fornecidas no Ghostfolio vs. A tabela de comparação é baseada em nossa pesquisa e análise independentes. Este site não é afiliado a ou qualquer outro produto mencionado na comparação. À medida que o cenário das ferramentas de finanças pessoais evolui, é essencial verificar quaisquer detalhes ou alterações específicas diretamente na página do produto correspondente. Os dados precisam de uma atualização? Ajude-nos a manter dados precisos sobre GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Observe que as informações fornecidas no Ghostfolio vs. A tabela de comparação é baseada em nossa pesquisa e análise independentes. Este site não é afiliado a ou qualquer outro produto mencionado na comparação. À medida que o cenário das ferramentas de finanças pessoais evolui, é essencial verificar quaisquer detalhes ou alterações específicas diretamente na página do produto correspondente. Os dados precisam de uma atualização? Ajude-nos a manter dados precisos sobre GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5528,7 +5528,7 @@ Global apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5832,8 +5832,8 @@ - Ghostfolio vs comparison table - Ghostfolio vs tabela de comparação + Ghostfolio vs comparison table + Ghostfolio vs tabela de comparação apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -6068,7 +6068,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6537,7 +6537,7 @@ Alternativo apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6545,7 +6545,7 @@ Aplicativo apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6553,7 +6553,7 @@ Orçamento apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6613,7 +6613,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6629,7 +6629,7 @@ Escritório Familiar apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6637,7 +6637,7 @@ Investidor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6649,7 +6649,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6661,7 +6661,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6669,7 +6669,7 @@ Privacidade apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6677,7 +6677,7 @@ Programas apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6685,7 +6685,7 @@ Ferramenta apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6693,7 +6693,7 @@ Experiência do usuário apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6701,7 +6701,7 @@ Fortuna apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6709,7 +6709,7 @@ Gestão de patrimônio apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6865,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7013,8 +7013,8 @@ - is Open Source Software - é software de código aberto + is Open Source Software + é software de código aberto apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7025,8 +7025,8 @@ - is not Open Source Software - não é software de código aberto + is not Open Source Software + não é software de código aberto apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7061,8 +7061,8 @@ - can be self-hosted - pode ser auto-hospedado + can be self-hosted + pode ser auto-hospedado apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7081,8 +7081,8 @@ - cannot be self-hosted - não pode ser auto-hospedado + cannot be self-hosted + não pode ser auto-hospedado apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7093,8 +7093,8 @@ - can be used anonymously - pode ser usado anonimamente + can be used anonymously + pode ser usado anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7105,8 +7105,8 @@ - cannot be used anonymously - não pode ser usado anonimamente + cannot be used anonymously + não pode ser usado anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7117,8 +7117,8 @@ - offers a free plan - oferece um plano gratuito + offers a free plan + oferece um plano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7129,8 +7129,8 @@ - does not offer a free plan - não oferece um plano gratuito + does not offer a free plan + não oferece um plano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index b2893c970..9a3ed7acf 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -499,7 +499,7 @@ Sil apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -1347,7 +1347,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -4015,7 +4015,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -4220,24 +4220,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Açık kaynaklı bir alternatif mi arıyorsunuz ? Ghostfolio güçlü bir portföy yönetim aracıdır ve bireylere yatırımlarını takip etmek, analiz etmek ve optimize etmek için kapsamlı bir platform sunar. İster deneyimli bir yatırımcı olun ister yeni başlıyor olun, Ghostfolio, bilinçli kararlar almanıza ve finansal geleceğinizi kontrol etmenize yardımcı olmak için sezgisel bir kullanıcı arayüzü ve geniş bir işlevsellik yelpazesi sunmaktadır. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Açık kaynaklı bir alternatif mi arıyorsunuz ? Ghostfolio güçlü bir portföy yönetim aracıdır ve bireylere yatırımlarını takip etmek, analiz etmek ve optimize etmek için kapsamlı bir platform sunar. İster deneyimli bir yatırımcı olun ister yeni başlıyor olun, Ghostfolio, bilinçli kararlar almanıza ve finansal geleceğinizi kontrol etmenize yardımcı olmak için sezgisel bir kullanıcı arayüzü ve geniş bir işlevsellik yelpazesi sunmaktadır. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio, karşısında açık kaynak kodlu maliyet etkin bir seçenek sunmaktadır. gibi kısıtlı bütçeye sahip, finansal özgürlük ve erken emeklilik (FIRE) amaçlayan kulanıcılar için özellikle uygundur. Ghostfolio, topluluk içindeki geliştiricilerin ve kişisel finans meraklılarının kolektif çabası sayesinde yeteneklerini, güvenliğini ve kullanıcı deneyimini sürekli olarak geliştirmektedir. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio, karşısında açık kaynak kodlu maliyet etkin bir seçenek sunmaktadır. gibi kısıtlı bütçeye sahip, finansal özgürlük ve erken emeklilik (FIRE) amaçlayan kulanıcılar için özellikle uygundur. Ghostfolio, topluluk içindeki geliştiricilerin ve kişisel finans meraklılarının kolektif çabası sayesinde yeteneklerini, güvenliğini ve kullanıcı deneyimini sürekli olarak geliştirmektedir. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Ghostfolio ve arasındaki ayrıntılı karşılaştırmanın yer aldığı aşağıdaki tabloya daha yakından bakarak Ghostfolio’nun karşısında kendisini nasıl konumlandırdığını kapsamlı bir şekilde değerlendirelim. Bu kapsamda özellikler, veri güvenliği, fiyat vb. hususları inceleyerek kişisel gereksinimleriniz için bilgiye dayalı bir seçim yapmanızı sağlayabileceği. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Ghostfolio ve arasındaki ayrıntılı karşılaştırmanın yer aldığı aşağıdaki tabloya daha yakından bakarak Ghostfolio’nun karşısında kendisini nasıl konumlandırdığını kapsamlı bir şekilde değerlendirelim. Bu kapsamda özellikler, veri güvenliği, fiyat vb. hususları inceleyerek kişisel gereksinimleriniz için bilgiye dayalı bir seçim yapmanızı sağlayabileceği. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4388,8 +4388,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Lütfen dikkat, Ghostfolio ve arasındaki karşılaştırmanın yer aldığı tablodaki bilgiler bağımsız araştırmalarımıza dayanmaktadır. Websitemizin ile ya da bu karşılaştırmada adı geçen herhangi bir ürün ve hizmet ile ilişkisi bulunmamaktadır. Kişisel finans araçlarının kapsamı geliştikçe, belirli ayrıntıların veya değişikliklerin doğrudan ilgili ürün sayfasından doğrulanması önemlidir. Verilerin yenilenmesi mi gerekiyor? Doğru verileri sağlamamıza yardımcı olmak için sayfamızı ziyaret edin. GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Lütfen dikkat, Ghostfolio ve arasındaki karşılaştırmanın yer aldığı tablodaki bilgiler bağımsız araştırmalarımıza dayanmaktadır. Websitemizin ile ya da bu karşılaştırmada adı geçen herhangi bir ürün ve hizmet ile ilişkisi bulunmamaktadır. Kişisel finans araçlarının kapsamı geliştikçe, belirli ayrıntıların veya değişikliklerin doğrudan ilgili ürün sayfasından doğrulanması önemlidir. Verilerin yenilenmesi mi gerekiyor? Doğru verileri sağlamamıza yardımcı olmak için sayfamızı ziyaret edin. GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -4416,7 +4416,7 @@ Küresel apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5832,8 +5832,8 @@ - Ghostfolio vs comparison table - Ghostfolio ve karşılatırma tablosu + Ghostfolio vs comparison table + Ghostfolio ve karşılatırma tablosu apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -6068,7 +6068,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6537,7 +6537,7 @@ Alternatif apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6545,7 +6545,7 @@ App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6553,7 +6553,7 @@ Bütçeleme apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6613,7 +6613,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6629,7 +6629,7 @@ Aile Ofisi apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6637,7 +6637,7 @@ Yatırımcı apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6649,7 +6649,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6661,7 +6661,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6669,7 +6669,7 @@ Gizlilik apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6677,7 +6677,7 @@ Yazılım apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6685,7 +6685,7 @@ Araç apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6693,7 +6693,7 @@ Kullanıcı Deneyimi apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6701,7 +6701,7 @@ Zenginlik apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6709,7 +6709,7 @@ Zenginlik Yönetimi apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6865,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7013,8 +7013,8 @@ - is Open Source Software - , Açık Kaynak Kodlu Yazılımdır + is Open Source Software + , Açık Kaynak Kodlu Yazılımdır apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7025,8 +7025,8 @@ - is not Open Source Software - , Açık Kaynak Kodlu Yazılımdır + is not Open Source Software + , Açık Kaynak Kodlu Yazılımdır apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7061,8 +7061,8 @@ - can be self-hosted - kendi sunucunuzda barındırılabilir + can be self-hosted + kendi sunucunuzda barındırılabilir apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7081,8 +7081,8 @@ - cannot be self-hosted - kendi sunucunuzda barındırılmaz + cannot be self-hosted + kendi sunucunuzda barındırılmaz apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7093,8 +7093,8 @@ - can be used anonymously - gizli kullanımda kullanılabilir + can be used anonymously + gizli kullanımda kullanılabilir apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7105,8 +7105,8 @@ - cannot be used anonymously - gizli kullanımda kullanılmaz + cannot be used anonymously + gizli kullanımda kullanılmaz apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7117,8 +7117,8 @@ - offers a free plan - ücretsiz plan sunar + offers a free plan + ücretsiz plan sunar apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7129,8 +7129,8 @@ - does not offer a free plan - ücretsiz plan sunmaz + does not offer a free plan + ücretsiz plan sunmaz apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index 85530b392..422191958 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -623,7 +623,7 @@ Видалити apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -1759,7 +1759,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -5004,7 +5004,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -5364,7 +5364,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -5623,7 +5623,7 @@ Глобальний apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5635,7 +5635,7 @@ Альтернатива apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -5643,7 +5643,7 @@ Додаток apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -5651,7 +5651,7 @@ Бюджетування apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -5711,7 +5711,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -5727,7 +5727,7 @@ Сімейний офіс apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -5735,7 +5735,7 @@ Інвестор apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -5747,7 +5747,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -5759,7 +5759,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -5767,7 +5767,7 @@ Конфіденційність apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -5775,7 +5775,7 @@ Програмне забезпечення apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -5783,7 +5783,7 @@ Інструмент apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -5791,7 +5791,7 @@ Користувацький досвід apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -5799,7 +5799,7 @@ Багатство apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -5807,7 +5807,7 @@ Управління багатством apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -5827,24 +5827,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Ви шукаєте відкриту альтернативу для ? Ghostfolio є потужним інструментом управління портфелем, який надає індивідуумам комплексну платформу для відстеження, аналізу та оптимізації їхніх інвестицій. Незалежно від того, чи ви досвідчений інвестор, чи тільки починаєте, Ghostfolio пропонує зручний інтерфейс користувача та широкий спектр функціональностей для допомоги вам у прийнятті обґрунтованих рішень та взятті під контроль вашого фінансового майбутнього. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Ви шукаєте відкриту альтернативу для ? Ghostfolio є потужним інструментом управління портфелем, який надає індивідуумам комплексну платформу для відстеження, аналізу та оптимізації їхніх інвестицій. Незалежно від того, чи ви досвідчений інвестор, чи тільки починаєте, Ghostfolio пропонує зручний інтерфейс користувача та широкий спектр функціональностей для допомоги вам у прийнятті обґрунтованих рішень та взятті під контроль вашого фінансового майбутнього. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio є відкритим програмним забезпеченням (OSS), що надає економічно ефективну альтернативу для , роблячи його особливо підходящим для тих, хто обмежений у бюджеті, таких як прагнення до фінансової незалежності, раннього виходу на пенсію (FIRE). Використовуючи колективні зусилля спільноти розробників та ентузіастів особистих фінансів, Ghostfolio постійно покращує свої можливості, безпеку та користувацький досвід. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio є відкритим програмним забезпеченням (OSS), що надає економічно ефективну альтернативу для , роблячи його особливо підходящим для тих, хто обмежений у бюджеті, таких як прагнення до фінансової незалежності, раннього виходу на пенсію (FIRE). Використовуючи колективні зусилля спільноти розробників та ентузіастів особистих фінансів, Ghostfolio постійно покращує свої можливості, безпеку та користувацький досвід. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Давайте заглибимося у детальну порівняльну таблицю Ghostfolio проти нижче, щоб отримати повне розуміння того, як Ghostfolio позиціонує себе відносно . Ми дослідимо різні аспекти, такі як функції, конфіденційність даних, ціни тощо, що дозволить вам зробити добре обдуманий вибір для ваших особистих потреб. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Давайте заглибимося у детальну порівняльну таблицю Ghostfolio проти нижче, щоб отримати повне розуміння того, як Ghostfolio позиціонує себе відносно . Ми дослідимо різні аспекти, такі як функції, конфіденційність даних, ціни тощо, що дозволить вам зробити добре обдуманий вибір для ваших особистих потреб. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5863,8 +5863,8 @@ - Ghostfolio vs comparison table - Порівняльна таблиця Ghostfolio проти + Ghostfolio vs comparison table + Порівняльна таблиця Ghostfolio проти apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -5911,8 +5911,8 @@ - is Open Source Software - є відкритим програмним забезпеченням + is Open Source Software + є відкритим програмним забезпеченням apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -5959,8 +5959,8 @@ - is not Open Source Software - не є відкритим програмним забезпеченням + is not Open Source Software + не є відкритим програмним забезпеченням apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -6039,8 +6039,8 @@ - can be self-hosted - може бути self-hosted + can be self-hosted + може бути self-hosted apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -6059,8 +6059,8 @@ - cannot be self-hosted - не може бути self-hosted + cannot be self-hosted + не може бути self-hosted apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -6079,8 +6079,8 @@ - can be used anonymously - може використовуватися анонімно + can be used anonymously + може використовуватися анонімно apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -6091,8 +6091,8 @@ - cannot be used anonymously - не може використовуватися анонімно + cannot be used anonymously + не може використовуватися анонімно apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -6111,8 +6111,8 @@ - offers a free plan - пропонує безкоштовний план + offers a free plan + пропонує безкоштовний план apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -6123,8 +6123,8 @@ - does not offer a free plan - не пропонує безкоштовний план + does not offer a free plan + не пропонує безкоштовний план apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 @@ -6163,8 +6163,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Зазначаємо, що інформація, надана в порівняльній таблиці Ghostfolio проти базується на нашому незалежному дослідженні та аналізі. Цей веб-сайт не пов’язаний з або будь-яким іншим продуктом, згаданим у порівнянні. Оскільки ландшафт інструментів особистих фінансів еволюціонує, важливо перевіряти будь-які конкретні деталі або зміни безпосередньо на сторінці відповідного продукту. Потрібно оновити дані? Допоможіть нам підтримувати точні дані на GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Зазначаємо, що інформація, надана в порівняльній таблиці Ghostfolio проти базується на нашому незалежному дослідженні та аналізі. Цей веб-сайт не пов’язаний з або будь-яким іншим продуктом, згаданим у порівнянні. Оскільки ландшафт інструментів особистих фінансів еволюціонує, важливо перевіряти будь-які конкретні деталі або зміни безпосередньо на сторінці відповідного продукту. Потрібно оновити дані? Допоможіть нам підтримувати точні дані на GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -6875,7 +6875,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index c15799d5f..0749e5b1d 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -517,7 +517,7 @@ Delete apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -1406,7 +1406,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -4209,7 +4209,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -4375,21 +4375,21 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4407,7 +4407,7 @@ - Ghostfolio vs comparison table + Ghostfolio vs comparison table apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -4558,7 +4558,7 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -4582,7 +4582,7 @@ Global apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -5565,7 +5565,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -5960,7 +5960,7 @@ Wealth apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6019,7 +6019,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6033,35 +6033,35 @@ User Experience apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 App apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 Tool apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 Investor apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 Wealth Management apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6075,14 +6075,14 @@ Alternative apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 Family Office apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6093,21 +6093,21 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 Budgeting apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6118,14 +6118,14 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 Privacy apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6257,7 +6257,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -6382,7 +6382,7 @@ - offers a free plan + offers a free plan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -6393,7 +6393,7 @@ - does not offer a free plan + does not offer a free plan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 @@ -6432,7 +6432,7 @@ - can be self-hosted + can be self-hosted apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -6450,7 +6450,7 @@ - cannot be self-hosted + cannot be self-hosted apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -6461,7 +6461,7 @@ - can be used anonymously + can be used anonymously apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -6472,7 +6472,7 @@ - cannot be used anonymously + cannot be used anonymously apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -6483,7 +6483,7 @@ - is not Open Source Software + is not Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -6494,7 +6494,7 @@ - is Open Source Software + is Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index f21944458..a0d0381ee 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -548,7 +548,7 @@ 删除 apps/client/src/app/components/admin-market-data/admin-market-data.html - 300 + 301 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -1508,7 +1508,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 98 + 106 libs/common/src/lib/routes/routes.ts @@ -4592,7 +4592,7 @@ Expiration apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 198 + 199 @@ -4777,24 +4777,24 @@ - Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - 您是否正在寻找开源替代方案幽灵作品集是一个强大的投资组合管理工具,为个人提供一个全面的平台来跟踪、分析和优化他们的投资。无论您是经验丰富的投资者还是刚刚起步的投资者,Ghostfolio 都提供直观的用户界面和广泛的功能帮助您做出明智的决定并掌控您的财务未来。 + Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. + 您是否正在寻找开源替代方案幽灵作品集是一个强大的投资组合管理工具,为个人提供一个全面的平台来跟踪、分析和优化他们的投资。无论您是经验丰富的投资者还是刚刚起步的投资者,Ghostfolio 都提供直观的用户界面和广泛的功能帮助您做出明智的决定并掌控您的财务未来。 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 - Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio 是一款开源软件 (OSS),提供了一种经济高效的替代方案使其特别适合预算紧张的个人,例如追求财务独立,提前退休(FIRE) 。通过利用开发者社区和个人理财爱好者的集体努力,Ghostfolio 不断增强其功能、安全性和用户体验。 + Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. + Ghostfolio 是一款开源软件 (OSS),提供了一种经济高效的替代方案使其特别适合预算紧张的个人,例如追求财务独立,提前退休(FIRE) 。通过利用开发者社区和个人理财爱好者的集体努力,Ghostfolio 不断增强其功能、安全性和用户体验。 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 - Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - 让我们更深入地了解 Ghostfolio 与下面的比较表可帮助您全面了解 Ghostfolio 相对于其他产品的定位。我们将探讨功能、数据隐私、定价等各个方面,让您根据个人需求做出明智的选择。 + Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. + 让我们更深入地了解 Ghostfolio 与下面的比较表可帮助您全面了解 Ghostfolio 相对于其他产品的定位。我们将探讨功能、数据隐私、定价等各个方面,让您根据个人需求做出明智的选择。 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4813,8 +4813,8 @@ - Ghostfolio vs comparison table - Ghostfolio vs比较表 + Ghostfolio vs comparison table + Ghostfolio vs比较表 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -4977,8 +4977,8 @@ - Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - 请注意,在 Ghostfolio 与的比较表中提供的信息基于我们的独立研究和分析。该网站不隶属于或比较中提到的任何其他产品。随着个人理财工具格局的不断发展,直接从相应的产品页面验证任何具体的细节或变化至关重要。数据需要刷新吗?帮助我们在GitHub 上维护准确的数据。 + Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. + 请注意,在 Ghostfolio 与的比较表中提供的信息基于我们的独立研究和分析。该网站不隶属于或比较中提到的任何其他产品。随着个人理财工具格局的不断发展,直接从相应的产品页面验证任何具体的细节或变化至关重要。数据需要刷新吗?帮助我们在GitHub 上维护准确的数据。 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5005,7 +5005,7 @@ 全球的 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 60 + 51 libs/ui/src/lib/i18n.ts @@ -6093,7 +6093,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 92 + 100 @@ -6538,7 +6538,7 @@ 另类 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 86 + 94 @@ -6546,7 +6546,7 @@ 应用 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 87 + 95 @@ -6554,7 +6554,7 @@ 预算管理 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 88 + 96 @@ -6614,7 +6614,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 89 + 97 @@ -6630,7 +6630,7 @@ 家族办公室 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 90 + 98 @@ -6638,7 +6638,7 @@ 投资者 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 93 + 101 @@ -6650,7 +6650,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 94 + 102 @@ -6662,7 +6662,7 @@ apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 96 + 104 @@ -6670,7 +6670,7 @@ 隐私 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 97 + 105 @@ -6678,7 +6678,7 @@ 软件 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 99 + 107 @@ -6686,7 +6686,7 @@ 工具 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 100 + 108 @@ -6694,7 +6694,7 @@ 用户体验 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 101 + 109 @@ -6702,7 +6702,7 @@ 财富 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 102 + 110 @@ -6710,7 +6710,7 @@ 财富管理 apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts - 103 + 111 @@ -6866,7 +6866,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 233 + 234 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7014,8 +7014,8 @@ - is Open Source Software - 是开源软件 + is Open Source Software + 是开源软件 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7026,8 +7026,8 @@ - is not Open Source Software - 不是开源软件 + is not Open Source Software + 不是开源软件 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7062,8 +7062,8 @@ - can be self-hosted - 可以自托管 + can be self-hosted + 可以自托管 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7082,8 +7082,8 @@ - cannot be self-hosted - 无法自托管 + cannot be self-hosted + 无法自托管 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7094,8 +7094,8 @@ - can be used anonymously - 可以匿名使用 + can be used anonymously + 可以匿名使用 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7106,8 +7106,8 @@ - cannot be used anonymously - 无法匿名使用 + cannot be used anonymously + 无法匿名使用 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7118,8 +7118,8 @@ - offers a free plan - 提供免费计划 + offers a free plan + 提供免费计划 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7130,8 +7130,8 @@ - does not offer a free plan - 不提供免费计划 + does not offer a free plan + 不提供免费计划 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 From 6dac6c242ba0a3baf81ba083cdc277c6c841f51a Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:02:03 +0200 Subject: [PATCH 32/60] Task/improve language localization (20260623) (#7116) Update translations --- apps/client/src/locales/messages.ca.xlf | 10 +++++----- apps/client/src/locales/messages.de.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.es.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.fr.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.it.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.ko.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.nl.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.pl.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.pt.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.tr.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.uk.xlf | 26 ++++++++++++------------- apps/client/src/locales/messages.zh.xlf | 26 ++++++++++++------------- 12 files changed, 148 insertions(+), 148 deletions(-) diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index 9d17d4bc1..e33abee92 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -5198,7 +5198,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Estàs buscant una alternativa de codi obert a ? Ghostfolio és una potent eina de gestió de portafolis que proporciona a les persones una plataforma completa per fer el seguiment, analitzar i optimitzar les seves inversions. Tant si ets un inversor amb experiència com si tot just comences, Ghostfolio ofereix una interfície intuïtiva i una àmplia gamma de funcionalitats per ajudar-te a prendre decisions informades i a prendre el control del teu futur financer. + Estàs buscant una alternativa de codi obert a ? Ghostfolio és una potent eina de gestió de portafolis que proporciona a les persones una plataforma completa per fer el seguiment, analitzar i optimitzar les seves inversions. Tant si ets un inversor amb experiència com si tot just comences, Ghostfolio ofereix una interfície intuïtiva i una àmplia gamma de funcionalitats per ajudar-te a prendre decisions informades i a prendre el control del teu futur financer. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -5206,7 +5206,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio és un programari de codi obert (OSS), que proporciona una alternativa rendible a , especialment adequada per a persones amb un pressupost ajustat, com ara aquelles que segueixen el camí cap a la independència financera i jubilació anticipada (FIRE). Mitjançant els esforços col·lectius d’una comunitat de desenvolupadors i apassionats de les finances personals, Ghostfolio millora contínuament les seves capacitats, la seva seguretat i l’experiència d’usuari. + Ghostfolio és un programari de codi obert (OSS), que proporciona una alternativa rendible a , especialment adequada per a persones amb un pressupost ajustat, com ara aquelles que segueixen el camí cap a la independència financera i jubilació anticipada (FIRE). Mitjançant els esforços col·lectius d’una comunitat de desenvolupadors i apassionats de les finances personals, Ghostfolio millora contínuament les seves capacitats, la seva seguretat i l’experiència d’usuari. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -5214,7 +5214,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Explorem en profunditat la taula comparativa detallada de Ghostfolio vs que trobaràs a continuació per entendre a fons com es posiciona Ghostfolio en relació amb . Analitzarem diversos aspectes com ara funcionalitats, privadesa de dades, preus i molt més, per tal que puguis prendre una decisió ben informada segons les teves necessitats personals. + Explorem en profunditat la taula comparativa detallada de Ghostfolio vs que trobaràs a continuació per entendre a fons com es posiciona Ghostfolio en relació amb . Analitzarem diversos aspectes com ara funcionalitats, privadesa de dades, preus i molt més, per tal que puguis prendre una decisió ben informada segons les teves necessitats personals. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5234,7 +5234,7 @@ Ghostfolio vs comparison table - Taula comparativa Ghostfolio vs + Taula comparativa Ghostfolio vs apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -5398,7 +5398,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Tingues en compte que la informació proporcionada a la taula comparativa Ghostfolio vs es basa en la nostra investigació i anàlisi independents. Aquest lloc web no està afiliat a ni a cap altre producte esmentat en la comparació. A mesura que evoluciona el panorama de les eines de finances personals, és essencial verificar qualsevol detall o canvi específic directament a la pàgina del producte corresponent. Necessites actualitzar dades? Ajuda’ns a mantenir la informació precisa a GitHub. + Tingues en compte que la informació proporcionada a la taula comparativa Ghostfolio vs es basa en la nostra investigació i anàlisi independents. Aquest lloc web no està afiliat a ni a cap altre producte esmentat en la comparació. A mesura que evoluciona el panorama de les eines de finances personals, és essencial verificar qualsevol detall o canvi específic directament a la pàgina del producte corresponent. Necessites actualitzar dades? Ajuda’ns a mantenir la informació precisa a GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index eda5d502d..1010d96d8 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -5496,7 +5496,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Suchst du nach einer Open Source Alternative zu ? Ghostfolio ist ein leistungsstarkes Portfolio Management Tool, das Privatpersonen eine umfassende Plattform bietet, um ihre Investitionen zu verfolgen, zu analysieren und zu optimieren. Egal, ob du ein erfahrener Investor bist oder gerade erst anfängst, Ghostfolio bietet eine intuitive Benutzeroberfläche und eine Vielzahl an Funktionen, die dir dabei helfen, fundierte Entscheidungen zu treffen und die Kontrolle über deine finanzielle Zukunft zu übernehmen. + Suchst du nach einer Open Source Alternative zu ? Ghostfolio ist ein leistungsstarkes Portfolio Management Tool, das Privatpersonen eine umfassende Plattform bietet, um ihre Investitionen zu verfolgen, zu analysieren und zu optimieren. Egal, ob du ein erfahrener Investor bist oder gerade erst anfängst, Ghostfolio bietet eine intuitive Benutzeroberfläche und eine Vielzahl an Funktionen, die dir dabei helfen, fundierte Entscheidungen zu treffen und die Kontrolle über deine finanzielle Zukunft zu übernehmen. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -5504,7 +5504,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio ist eine Open Source Software (OSS), die eine kostengünstige Alternative zu darstellt und sich besonders für Personen mit knappem Budget eignet, wie z.B. für diejenigen, die finanzielle Unabhängigkeit und einen frühen Ruhestand anstreben (FIRE). Ghostfolio nutzt die gemeinsamen Aktivitäten einer Community von Entwicklern und Finanzenthusiasten, um seine Funktionalität, Sicherheit und Benutzerfreundlichkeit kontinuierlich zu verbessern. + Ghostfolio ist eine Open Source Software (OSS), die eine kostengünstige Alternative zu darstellt und sich besonders für Personen mit knappem Budget eignet, wie z.B. für diejenigen, die finanzielle Unabhängigkeit und einen frühen Ruhestand anstreben (FIRE). Ghostfolio nutzt die gemeinsamen Aktivitäten einer Community von Entwicklern und Finanzenthusiasten, um seine Funktionalität, Sicherheit und Benutzerfreundlichkeit kontinuierlich zu verbessern. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -5512,7 +5512,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Wir möchten uns in der untenstehenden Ghostfolio vs Vergleichstabelle ein detailliertes Bild davon machen, wie Ghostfolio im Vergleich zu positioniert ist. Wir werden dabei verschiedene Aspekte wie Funktionen, Datenschutz, Preise und weiteres untersuchen, damit du eine gut informierte Entscheidung für deine persönlichen Anforderungen treffen kannst. + Wir möchten uns in der untenstehenden Ghostfolio vs Vergleichstabelle ein detailliertes Bild davon machen, wie Ghostfolio im Vergleich zu positioniert ist. Wir werden dabei verschiedene Aspekte wie Funktionen, Datenschutz, Preise und weiteres untersuchen, damit du eine gut informierte Entscheidung für deine persönlichen Anforderungen treffen kannst. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5533,7 +5533,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Bitte beachte, dass die bereitgestellten Ghostfolio vs Informationen auf unserer unabhängigen Recherche und Analyse beruhen. Diese Webseite steht in keiner Verbindung zu oder einem anderen im Vergleich erwähnten Produkt. Da sich die Landschaft der Personal Finance Tools ständig weiterentwickelt, ist es wichtig, alle spezifischen Details oder Änderungen direkt auf der jeweiligen Produktseite zu überprüfen. Brauchen die Daten eine Auffrischung? Unterstütze uns bei der Pflege der aktuellen Daten auf GitHub. + Bitte beachte, dass die bereitgestellten Ghostfolio vs Informationen auf unserer unabhängigen Recherche und Analyse beruhen. Diese Webseite steht in keiner Verbindung zu oder einem anderen im Vergleich erwähnten Produkt. Da sich die Landschaft der Personal Finance Tools ständig weiterentwickelt, ist es wichtig, alle spezifischen Details oder Änderungen direkt auf der jeweiligen Produktseite zu überprüfen. Brauchen die Daten eine Auffrischung? Unterstütze uns bei der Pflege der aktuellen Daten auf GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5857,7 +5857,7 @@ Ghostfolio vs comparison table - Ghostfolio vs Vergleichstabelle + Ghostfolio vs Vergleichstabelle apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -7038,7 +7038,7 @@ is Open Source Software - ist Open Source Software + ist Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7050,7 +7050,7 @@ is not Open Source Software - ist keine Open Source Software + ist keine Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7086,7 +7086,7 @@ can be self-hosted - kann selbst gehostet werden + kann selbst gehostet werden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7106,7 +7106,7 @@ cannot be self-hosted - kann nicht selbst gehostet werden + kann nicht selbst gehostet werden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7118,7 +7118,7 @@ can be used anonymously - kann anonym genutzt werden + kann anonym genutzt werden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7130,7 +7130,7 @@ cannot be used anonymously - kann nicht anonym genutzt werden + kann nicht anonym genutzt werden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7142,7 +7142,7 @@ offers a free plan - hat ein kostenloses Angebot + hat ein kostenloses Angebot apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7154,7 +7154,7 @@ does not offer a free plan - hat kein kostenloses Angebot + hat kein kostenloses Angebot apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index a0ed117fa..048b748eb 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -5473,7 +5473,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - ¿Estás buscando una alternativa de código abierto a ? Ghostfolio es una potente herramienta de gestión de carteras que proporciona a los usuarios una plataforma completa para seguir, analizar y optimizar sus inversiones. Ya seas un inversor con experiencia o estés comenzando, Ghostfolio ofrece una interfaz intuitiva y una amplia gama de funcionalidades para ayudarte a tomar decisiones informadas y tener el control de tu futuro financiero. + ¿Estás buscando una alternativa de código abierto a ? Ghostfolio es una potente herramienta de gestión de carteras que proporciona a los usuarios una plataforma completa para seguir, analizar y optimizar sus inversiones. Ya seas un inversor con experiencia o estés comenzando, Ghostfolio ofrece una interfaz intuitiva y una amplia gama de funcionalidades para ayudarte a tomar decisiones informadas y tener el control de tu futuro financiero. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -5481,7 +5481,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio es un software de código abierto (OSS), que ofrece una alternativa rentable a lo que lo hace especialmente adecuado para personas con un presupuesto ajustado, como aquellas que buscan la Independencia Financiera y Jubilación Anticipada (FIRE). Al aprovechar los esfuerzos colectivos de una comunidad de desarrolladores y entusiastas de las finanzas personales, Ghostfolio mejora continuamente sus capacidades, seguridad y experiencia de usuario. + Ghostfolio es un software de código abierto (OSS), que ofrece una alternativa rentable a lo que lo hace especialmente adecuado para personas con un presupuesto ajustado, como aquellas que buscan la Independencia Financiera y Jubilación Anticipada (FIRE). Al aprovechar los esfuerzos colectivos de una comunidad de desarrolladores y entusiastas de las finanzas personales, Ghostfolio mejora continuamente sus capacidades, seguridad y experiencia de usuario. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -5489,7 +5489,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Analicemos en detalle la tabla comparativa entre Ghostfolio y que encontrarás a continuación, para obtener una comprensión completa de cómo se posiciona Ghostfolio en relación con . Exploraremos diversos aspectos como funcionalidades, privacidad de los datos, precios y más, lo que te permitirá tomar una decisión bien fundamentada según tus necesidades personales. + Analicemos en detalle la tabla comparativa entre Ghostfolio y que encontrarás a continuación, para obtener una comprensión completa de cómo se posiciona Ghostfolio en relación con . Exploraremos diversos aspectos como funcionalidades, privacidad de los datos, precios y más, lo que te permitirá tomar una decisión bien fundamentada según tus necesidades personales. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5510,7 +5510,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Ten en cuenta que la información proporcionada en la tabla comparativa entre Ghostfolio y se basa en nuestra investigación y análisis independientes. Este sitio web no está afiliado con ni con ningún otro producto mencionado en la comparación. Dado que el panorama de las herramientas de finanzas personales evoluciona constantemente, es fundamental verificar cualquier detalle específico o cambio directamente en la página oficial del producto correspondiente. ¿Los datos necesitan una actualización? Ayúdanos a mantener la información precisa en GitHub. + Ten en cuenta que la información proporcionada en la tabla comparativa entre Ghostfolio y se basa en nuestra investigación y análisis independientes. Este sitio web no está afiliado con ni con ningún otro producto mencionado en la comparación. Dado que el panorama de las herramientas de finanzas personales evoluciona constantemente, es fundamental verificar cualquier detalle específico o cambio directamente en la página oficial del producto correspondiente. ¿Los datos necesitan una actualización? Ayúdanos a mantener la información precisa en GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5834,7 +5834,7 @@ Ghostfolio vs comparison table - Tabla comparativa de Ghostfolio vs + Tabla comparativa de Ghostfolio vs apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -7015,7 +7015,7 @@ is Open Source Software - es software de código abierto + es software de código abierto apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7027,7 +7027,7 @@ is not Open Source Software - no es software de código abierto + no es software de código abierto apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7063,7 +7063,7 @@ can be self-hosted - se puede autoalojar + se puede autoalojar apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7083,7 +7083,7 @@ cannot be self-hosted - no se puede autoalojar + no se puede autoalojar apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7095,7 +7095,7 @@ can be used anonymously - se puede usar de forma anónima + se puede usar de forma anónima apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7107,7 +7107,7 @@ cannot be used anonymously - no se puede usar de forma anónima + no se puede usar de forma anónima apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7119,7 +7119,7 @@ offers a free plan - ofrece un plan gratuito + ofrece un plan gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7131,7 +7131,7 @@ does not offer a free plan - no ofrece un plan gratuito + no ofrece un plan gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index ef96b4a18..3c1305b2b 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -5472,7 +5472,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Cherchez-vous des alternatives open source à ? Ghostfolio est un outil de gestion de portefeuille puissant offrant aux particuliers une plateforme complète pour suivre, analyser et optimiser ses investissements. Que vous soyez un investisseur expérimenté ou que vous débutiez, Ghostfolio offre une interface utilisateur intuitive et une large gamme de fonctionnalités pour vous aider à prendre des décisions éclairées et à prendre le contrôle de votre avenir financier. + Cherchez-vous des alternatives open source à ? Ghostfolio est un outil de gestion de portefeuille puissant offrant aux particuliers une plateforme complète pour suivre, analyser et optimiser ses investissements. Que vous soyez un investisseur expérimenté ou que vous débutiez, Ghostfolio offre une interface utilisateur intuitive et une large gamme de fonctionnalités pour vous aider à prendre des décisions éclairées et à prendre le contrôle de votre avenir financier. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -5480,7 +5480,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio est un logiciel open source (OSS), offrant une alternative économique, le rendant particulièrement adaptée aux personnes disposant d’un budget serré, telles que celles qui cherchent à atteindre le mouvement Financial Independence, Retire Early (FIRE). En s’appuyant sur les efforts collectifs d’une communauté de développeurs et de passionnés de finances personnelles, Ghostfolio améliore continuellement ses fonctionnalités, sa sécurité et son expérience utilisateur. + Ghostfolio est un logiciel open source (OSS), offrant une alternative économique, le rendant particulièrement adaptée aux personnes disposant d’un budget serré, telles que celles qui cherchent à atteindre le mouvement Financial Independence, Retire Early (FIRE). En s’appuyant sur les efforts collectifs d’une communauté de développeurs et de passionnés de finances personnelles, Ghostfolio améliore continuellement ses fonctionnalités, sa sécurité et son expérience utilisateur. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -5488,7 +5488,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Regardons plus en détails ce que proposent Ghostfolio vs via la table de comparaison ci-dessous pour comprendre comment Ghostfolio se positionne par rapport à . Nous examinerons divers aspects tels que les fonctionnalités, la confidentialité des données, le prix, etc., afin de vous permettre de faire un choix éclairé en fonction de vos besoins personnels. + Regardons plus en détails ce que proposent Ghostfolio vs via la table de comparaison ci-dessous pour comprendre comment Ghostfolio se positionne par rapport à . Nous examinerons divers aspects tels que les fonctionnalités, la confidentialité des données, le prix, etc., afin de vous permettre de faire un choix éclairé en fonction de vos besoins personnels. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5509,7 +5509,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Veuillez noter que les informations fournies dans le Ghostfolio vs via la table de comparaison se basent de nos recherches et analyses indépendantes. Ce site n’est pas affilié à ou tout autre produit mentionné dans la comparaison. Le paysage des outils de finances personnelles évoluant, il est essentiel de vérifier tout détail ou changement spécifique directement sur la page du produit concerné. Certaines données doivent être mises à jour ? Aidez-nous à maintenir les données exactes sur GitHub. + Veuillez noter que les informations fournies dans le Ghostfolio vs via la table de comparaison se basent de nos recherches et analyses indépendantes. Ce site n’est pas affilié à ou tout autre produit mentionné dans la comparaison. Le paysage des outils de finances personnelles évoluant, il est essentiel de vérifier tout détail ou changement spécifique directement sur la page du produit concerné. Certaines données doivent être mises à jour ? Aidez-nous à maintenir les données exactes sur GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5833,7 +5833,7 @@ Ghostfolio vs comparison table - Ghostfolio vs tableau comparatif + Ghostfolio vs tableau comparatif apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -7014,7 +7014,7 @@ is Open Source Software - est un logiciel open source + est un logiciel open source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7026,7 +7026,7 @@ is not Open Source Software - n’est pas un logiciel open source + n’est pas un logiciel open source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7062,7 +7062,7 @@ can be self-hosted - peut être auto-hébergé + peut être auto-hébergé apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7082,7 +7082,7 @@ cannot be self-hosted - ne peut pas être auto-hébergé + ne peut pas être auto-hébergé apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7094,7 +7094,7 @@ can be used anonymously - peut être utilisé de manière anonyme + peut être utilisé de manière anonyme apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7106,7 +7106,7 @@ cannot be used anonymously - ne peut pas être utilisé de manière anonyme + ne peut pas être utilisé de manière anonyme apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7118,7 +7118,7 @@ offers a free plan - propose un plan gratuit + propose un plan gratuit apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7130,7 +7130,7 @@ does not offer a free plan - ne propose pas de plan gratuit + ne propose pas de plan gratuit apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index e009676df..7bdd1d625 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -5473,7 +5473,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Stai cercando un’alternativa open source a ? Ghostfolio è un potente strumento di gestione del portafoglio che fornisce alle persone una piattaforma completa per monitorare, analizzare e ottimizzare i propri investimenti. Che tu sia un investitore esperto o alle prime armi, Ghostfolio offre un’interfaccia utente intuitiva e un’ampia gamma di funzionalità per aiutarti a prendere decisioni informate e il controllo del tuo futuro finanziario. + Stai cercando un’alternativa open source a ? Ghostfolio è un potente strumento di gestione del portafoglio che fornisce alle persone una piattaforma completa per monitorare, analizzare e ottimizzare i propri investimenti. Che tu sia un investitore esperto o alle prime armi, Ghostfolio offre un’interfaccia utente intuitiva e un’ampia gamma di funzionalità per aiutarti a prendere decisioni informate e il controllo del tuo futuro finanziario. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -5481,7 +5481,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio è un software open source (OSS) che offre un’alternativa economicamente vantaggiosa a particolarmente adatta a persone con un budget limitato, come quelle che perseguono l’indipendenza finanziaria e il pensionamento anticipato (FIRE). Grazie agli sforzi collettivi di una comunità di sviluppatori e di appassionati di finanza personale, Ghostfolio migliora continuamente le sue capacità, la sua sicurezza e la sua esperienza utente. + Ghostfolio è un software open source (OSS) che offre un’alternativa economicamente vantaggiosa a particolarmente adatta a persone con un budget limitato, come quelle che perseguono l’indipendenza finanziaria e il pensionamento anticipato (FIRE). Grazie agli sforzi collettivi di una comunità di sviluppatori e di appassionati di finanza personale, Ghostfolio migliora continuamente le sue capacità, la sua sicurezza e la sua esperienza utente. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -5489,7 +5489,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Analizziamo nel dettaglio la tabella di confronto qui sotto per comprendere a fondo come Ghostfolio si posiziona rispetto a . Esploreremo vari aspetti come le caratteristiche, la privacy dei dati, il prezzo e altro ancora, permettendoti di fare una scelta ben informata per le tue esigenze personali. + Analizziamo nel dettaglio la tabella di confronto qui sotto per comprendere a fondo come Ghostfolio si posiziona rispetto a . Esploreremo vari aspetti come le caratteristiche, la privacy dei dati, il prezzo e altro ancora, permettendoti di fare una scelta ben informata per le tue esigenze personali. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5510,7 +5510,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Nota bene: le informazioni fornite si basano sulle nostre ricerche e analisi indipendenti. Questo sito web non è affiliato con o a qualsiasi altro prodotto citato nel confronto. Poiché il panorama degli strumenti di finanza personale si evolve, è essenziale verificare qualsiasi dettaglio o modifica specifica direttamente nella pagina del prodotto in questione. I dati hanno bisogno di essere aggiornati? Aiutaci a mantenere i dati accurati su GitHub. + Nota bene: le informazioni fornite si basano sulle nostre ricerche e analisi indipendenti. Questo sito web non è affiliato con o a qualsiasi altro prodotto citato nel confronto. Poiché il panorama degli strumenti di finanza personale si evolve, è essenziale verificare qualsiasi dettaglio o modifica specifica direttamente nella pagina del prodotto in questione. I dati hanno bisogno di essere aggiornati? Aiutaci a mantenere i dati accurati su GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5834,7 +5834,7 @@ Ghostfolio vs comparison table - Ghostfolio vs tabella di comparazione + Ghostfolio vs tabella di comparazione apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -7015,7 +7015,7 @@ is Open Source Software - è un programma Open Source + è un programma Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7027,7 +7027,7 @@ is not Open Source Software - non è un programma Open Source + non è un programma Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7063,7 +7063,7 @@ can be self-hosted - può essere ospitato in proprio + può essere ospitato in proprio apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7083,7 +7083,7 @@ cannot be self-hosted - non può essere ospitato in proprio + non può essere ospitato in proprio apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7095,7 +7095,7 @@ can be used anonymously - può essere usato anonimamente + può essere usato anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7107,7 +7107,7 @@ cannot be used anonymously - non può essere usato anonimamente + non può essere usato anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7119,7 +7119,7 @@ offers a free plan - ha un piano gratuito + ha un piano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7131,7 +7131,7 @@ does not offer a free plan - non ha un piano gratuito + non ha un piano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.ko.xlf b/apps/client/src/locales/messages.ko.xlf index f3e4cea1a..f45a22d1c 100644 --- a/apps/client/src/locales/messages.ko.xlf +++ b/apps/client/src/locales/messages.ko.xlf @@ -4794,7 +4794,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - 에 대한 오픈소스 대안을 찾고 계십니까? Ghostfolio는 개인에게 투자를 추적, 분석, 최적화할 수 있는 포괄적인 플랫폼을 제공하는 강력한 포트폴리오 관리 도구입니다. 숙련된 투자자이든 이제 막 시작한 투자자이든 Ghostfolio는 직관적인 사용자 인터페이스와 다양한 기능을 제공하여 정보에 입각한 결정을 내리고 재정적 미래를 관리하는 데 도움을 줍니다. + 에 대한 오픈소스 대안을 찾고 계십니까? Ghostfolio는 개인에게 투자를 추적, 분석, 최적화할 수 있는 포괄적인 플랫폼을 제공하는 강력한 포트폴리오 관리 도구입니다. 숙련된 투자자이든 이제 막 시작한 투자자이든 Ghostfolio는 직관적인 사용자 인터페이스와 다양한 기능을 제공하여 정보에 입각한 결정을 내리고 재정적 미래를 관리하는 데 도움을 줍니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -4802,7 +4802,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio는 오픈 소스 소프트웨어로, 에 대한 비용 효율적인 대안을 제공하므로 재정적 독립, 조기 퇴직(FIRE)을 추구하는 사람과 같이 예산이 부족한 개인에게 특히 적합합니다. Ghostfolio는 개발자 커뮤니티와 개인 금융 애호가들의 공동 노력을 활용하여 기능, 보안 및 사용자 경험을 지속적으로 향상시킵니다. + Ghostfolio는 오픈 소스 소프트웨어로, 에 대한 비용 효율적인 대안을 제공하므로 재정적 독립, 조기 퇴직(FIRE)을 추구하는 사람과 같이 예산이 부족한 개인에게 특히 적합합니다. Ghostfolio는 개발자 커뮤니티와 개인 금융 애호가들의 공동 노력을 활용하여 기능, 보안 및 사용자 경험을 지속적으로 향상시킵니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -4810,7 +4810,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Ghostfolio가 과 관련하여 어떻게 위치하는지 철저하게 이해하기 위해 아래의 자세한 Ghostfolio 대 비교표를 자세히 살펴보겠습니다. 우리는 기능, 데이터 개인 정보 보호, 가격 등과 같은 다양한 측면을 탐색하여 귀하의 개인 요구 사항에 맞는 정보를 바탕으로 선택할 수 있도록 할 것입니다. + Ghostfolio가 과 관련하여 어떻게 위치하는지 철저하게 이해하기 위해 아래의 자세한 Ghostfolio 대 비교표를 자세히 살펴보겠습니다. 우리는 기능, 데이터 개인 정보 보호, 가격 등과 같은 다양한 측면을 탐색하여 귀하의 개인 요구 사항에 맞는 정보를 바탕으로 선택할 수 있도록 할 것입니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4830,7 +4830,7 @@ Ghostfolio vs comparison table - Ghostfolio와 비교표 + Ghostfolio와 비교표 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -4994,7 +4994,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Ghostfolio와 비교표에 제공된 정보는 당사의 독립적인 연구 및 분석을 기반으로 한 것입니다. 이 웹사이트는 또는 비교에 언급된 다른 제품과 관련이 없습니다. 개인 금융 도구의 환경이 발전함에 따라 각 제품 페이지에서 직접 특정 세부 정보나 변경 사항을 확인하는 것이 중요합니다. 데이터를 새로 고쳐야 합니까? 깃허브에서 정확한 데이터를 유지할 수 있도록 도와주세요. + Ghostfolio와 비교표에 제공된 정보는 당사의 독립적인 연구 및 분석을 기반으로 한 것입니다. 이 웹사이트는 또는 비교에 언급된 다른 제품과 관련이 없습니다. 개인 금융 도구의 환경이 발전함에 따라 각 제품 페이지에서 직접 특정 세부 정보나 변경 사항을 확인하는 것이 중요합니다. 데이터를 새로 고쳐야 합니까? 깃허브에서 정확한 데이터를 유지할 수 있도록 도와주세요. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -7023,7 +7023,7 @@ offers a free plan - 은(는) 무료 요금제를 제공합니다 + 은(는) 무료 요금제를 제공합니다 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7035,7 +7035,7 @@ does not offer a free plan - 은(는) 무료 요금제를 제공하지 않습니다. + 은(는) 무료 요금제를 제공하지 않습니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 @@ -7079,7 +7079,7 @@ can be self-hosted - 은(는) 자체 호스팅 가능 + 은(는) 자체 호스팅 가능 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7099,7 +7099,7 @@ cannot be self-hosted - 은(는) 자체 호스팅할 수 없습니다. + 은(는) 자체 호스팅할 수 없습니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7111,7 +7111,7 @@ can be used anonymously - 은(는) 익명으로 사용할 수 있습니다. + 은(는) 익명으로 사용할 수 있습니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7123,7 +7123,7 @@ cannot be used anonymously - 은(는) 익명으로 사용할 수 없습니다. + 은(는) 익명으로 사용할 수 없습니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7135,7 +7135,7 @@ is not Open Source Software - 은(는) 오픈 소스 소프트웨어가 아닙니다. + 은(는) 오픈 소스 소프트웨어가 아닙니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7147,7 +7147,7 @@ is Open Source Software - 은(는) 오픈 소스 소프트웨어입니다. + 은(는) 오픈 소스 소프트웨어입니다. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index 326134729..2cd6901f6 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -5472,7 +5472,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Ben je op zoek naar een open source alternatief voor ? Ghostfolio is een krachtige tool voor portefeuillebeheer die particulieren een uitgebreid platform biedt om hun beleggingen bij te houden, te analyseren en te optimaliseren. Of je nu een ervaren belegger bent of net begint, Ghostfolio biedt een intuïtieve gebruikersinterface en uitgebreide functionaliteiten om je te helpen weloverwogen beslissingen te nemen en je financiële toekomst in eigen handen te nemen. + Ben je op zoek naar een open source alternatief voor ? Ghostfolio is een krachtige tool voor portefeuillebeheer die particulieren een uitgebreid platform biedt om hun beleggingen bij te houden, te analyseren en te optimaliseren. Of je nu een ervaren belegger bent of net begint, Ghostfolio biedt een intuïtieve gebruikersinterface en uitgebreide functionaliteiten om je te helpen weloverwogen beslissingen te nemen en je financiële toekomst in eigen handen te nemen. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -5480,7 +5480,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio is open source software (OSS) en biedt een kosteneffectief alternatief voor waardoor het bijzonder geschikt is voor mensen met een krap budget, zoals degenen Financiële onafhankelijkheid nastreven, vroeg met pensioen gaan (FIRE). Door gebruik te maken van de collectieve inspanningen van een gemeenschap van ontwikkelaars en liefhebbers van persoonlijke financiën, verbetert Ghostfolio voortdurend de mogelijkheden, veiligheid en gebruikerservaring. + Ghostfolio is open source software (OSS) en biedt een kosteneffectief alternatief voor waardoor het bijzonder geschikt is voor mensen met een krap budget, zoals degenen Financiële onafhankelijkheid nastreven, vroeg met pensioen gaan (FIRE). Door gebruik te maken van de collectieve inspanningen van een gemeenschap van ontwikkelaars en liefhebbers van persoonlijke financiën, verbetert Ghostfolio voortdurend de mogelijkheden, veiligheid en gebruikerservaring. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -5488,7 +5488,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Laten we eens dieper duiken in de gedetailleerde vergelijkingstabel hieronder om een beter begrip te krijgen hoe Ghostfolio zichzelf positioneert ten opzichte van . We gaan in op verschillende aspecten zoals functies, gegevensprivacy, prijzen en meer, zodat je een weloverwogen keuze kunt maken voor jouw persoonlijke behoeften. + Laten we eens dieper duiken in de gedetailleerde vergelijkingstabel hieronder om een beter begrip te krijgen hoe Ghostfolio zichzelf positioneert ten opzichte van . We gaan in op verschillende aspecten zoals functies, gegevensprivacy, prijzen en meer, zodat je een weloverwogen keuze kunt maken voor jouw persoonlijke behoeften. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5509,7 +5509,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Houd er rekening mee dat de verstrekte informatie in deze Ghostfolio vs is gebaseerd op ons onafhankelijk onderzoek en analyse. Deze website is niet gelieerd aan of een ander product dat in de vergelijking wordt genoemd. Aangezien het landschap van tools voor persoonlijke financiën evolueert, is het essentieel om specifieke details of wijzigingen rechtstreeks op de betreffende productpagina te controleren. Hebben je gegevens een opfrisbeurt nodig? Help ons de gegevens nauwkeurig te houden op GitHub. + Houd er rekening mee dat de verstrekte informatie in deze Ghostfolio vs is gebaseerd op ons onafhankelijk onderzoek en analyse. Deze website is niet gelieerd aan of een ander product dat in de vergelijking wordt genoemd. Aangezien het landschap van tools voor persoonlijke financiën evolueert, is het essentieel om specifieke details of wijzigingen rechtstreeks op de betreffende productpagina te controleren. Hebben je gegevens een opfrisbeurt nodig? Help ons de gegevens nauwkeurig te houden op GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5833,7 +5833,7 @@ Ghostfolio vs comparison table - Ghostfolio vs vergelijkingstabel + Ghostfolio vs vergelijkingstabel apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -7014,7 +7014,7 @@ is Open Source Software - is Open Source Software + is Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7026,7 +7026,7 @@ is not Open Source Software - is geen Open Source Software + is geen Open Source Software apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7062,7 +7062,7 @@ can be self-hosted - kan zelf gehost worden + kan zelf gehost worden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7082,7 +7082,7 @@ cannot be self-hosted - kan niet zelf gehost worden + kan niet zelf gehost worden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7094,7 +7094,7 @@ can be used anonymously - kan anoniem gebruikt worden + kan anoniem gebruikt worden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7106,7 +7106,7 @@ cannot be used anonymously - kan niet anoniem gebruik worden + kan niet anoniem gebruik worden apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7118,7 +7118,7 @@ offers a free plan - biedt een gratis abonnement aan + biedt een gratis abonnement aan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7130,7 +7130,7 @@ does not offer a free plan - biedt geen gratis abonnement aan + biedt geen gratis abonnement aan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index 385c1fad4..ff36777a4 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -4761,7 +4761,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Szukasz alternatywy typu open source dla ? Ghostfolio to potężne narzędzie do zarządzania portfelem, które zapewnia osobom fizycznym kompleksową platformę do śledzenia, analizowania i optymalizacji ich inwestycji. Niezależnie od tego, czy jesteś doświadczonym inwestorem, czy dopiero zaczynasz, Ghostfolio oferuje intuicyjny interfejs użytkownika i szeroki zakres funkcjonalności, które pomogą Ci podejmować przemyślane decyzje i przejąć kontrolę nad swoją finansową przyszłością. + Szukasz alternatywy typu open source dla ? Ghostfolio to potężne narzędzie do zarządzania portfelem, które zapewnia osobom fizycznym kompleksową platformę do śledzenia, analizowania i optymalizacji ich inwestycji. Niezależnie od tego, czy jesteś doświadczonym inwestorem, czy dopiero zaczynasz, Ghostfolio oferuje intuicyjny interfejs użytkownika i szeroki zakres funkcjonalności, które pomogą Ci podejmować przemyślane decyzje i przejąć kontrolę nad swoją finansową przyszłością. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -4769,7 +4769,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio to oprogramowanie o otwartym kodzie źródłowym (OSS), stanowiące opłacalną alternatywę dla , czyniąc go szczególnie odpowiednim dla osób o ograniczonym budżecie, takich jak osoby dążące do finansowej niezależności i wcześniejszej emerytury (Financial Independence, Retire Early - FIRE). Wykorzystując wspólne wysiłki społeczności programistów i entuzjastów finansów osobistych, Ghostfolio stale zwiększa swoje możliwości, bezpieczeństwo i komfort użytkowania. + Ghostfolio to oprogramowanie o otwartym kodzie źródłowym (OSS), stanowiące opłacalną alternatywę dla , czyniąc go szczególnie odpowiednim dla osób o ograniczonym budżecie, takich jak osoby dążące do finansowej niezależności i wcześniejszej emerytury (Financial Independence, Retire Early - FIRE). Wykorzystując wspólne wysiłki społeczności programistów i entuzjastów finansów osobistych, Ghostfolio stale zwiększa swoje możliwości, bezpieczeństwo i komfort użytkowania. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -4777,7 +4777,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Zagłębmy się w szczegółową tabelę porównawczą Ghostfolio vs poniżej, aby dokładnie zrozumieć, jak Ghostfolio pozycjonuje się w stosunku do . Przeanalizujemy różne aspekty, takie jak funkcje, prywatność danych, opłaty i inne, umożliwiając Tobie dokonanie przemyślanego wyboru pod kątem osobistych wymagań. + Zagłębmy się w szczegółową tabelę porównawczą Ghostfolio vs poniżej, aby dokładnie zrozumieć, jak Ghostfolio pozycjonuje się w stosunku do . Przeanalizujemy różne aspekty, takie jak funkcje, prywatność danych, opłaty i inne, umożliwiając Tobie dokonanie przemyślanego wyboru pod kątem osobistych wymagań. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4797,7 +4797,7 @@ Ghostfolio vs comparison table - Ghostfolio vs - tabela porównawcza + Ghostfolio vs - tabela porównawcza apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -4949,7 +4949,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Należy pamiętać, że informacje zawarte w tabeli porównawczej Ghostfolio vs są oparte na naszych niezależnych badaniach i analizach. Ta strona internetowa nie jest powiązana z ani żadnym innym produktem wymienionym w porównaniu. Ponieważ krajobraz narzędzi do finansów osobistych ewoluuje, ważne jest, aby weryfikować wszelkie szczegóły lub zmiany bezpośrednio na stronie danego produktu. Informacje wymagają aktualizacji? Pomóż nam utrzymywać dokładne dane na GitHub. + Należy pamiętać, że informacje zawarte w tabeli porównawczej Ghostfolio vs są oparte na naszych niezależnych badaniach i analizach. Ta strona internetowa nie jest powiązana z ani żadnym innym produktem wymienionym w porównaniu. Ponieważ krajobraz narzędzi do finansów osobistych ewoluuje, ważne jest, aby weryfikować wszelkie szczegóły lub zmiany bezpośrednio na stronie danego produktu. Informacje wymagają aktualizacji? Pomóż nam utrzymywać dokładne dane na GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -7014,7 +7014,7 @@ is Open Source Software - jest Oprogramowaniem Open Source + jest Oprogramowaniem Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7026,7 +7026,7 @@ is not Open Source Software - nie jest Oprogramowaniem Open Source + nie jest Oprogramowaniem Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7062,7 +7062,7 @@ can be self-hosted - może być hostowana samodzielnie + może być hostowana samodzielnie apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7082,7 +7082,7 @@ cannot be self-hosted - nie może być hostowany samodzielnie + nie może być hostowany samodzielnie apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7094,7 +7094,7 @@ can be used anonymously - może być używana anonimowo + może być używana anonimowo apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7106,7 +7106,7 @@ cannot be used anonymously - nie może być używana anonimowo + nie może być używana anonimowo apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7118,7 +7118,7 @@ offers a free plan - oferuje darmowy plan + oferuje darmowy plan apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7130,7 +7130,7 @@ does not offer a free plan - nie oferuje darmowego planu + nie oferuje darmowego planu apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index bfcfdf7a5..147c11edb 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -5472,7 +5472,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Você está procurando uma alternativa de código aberto para ? Ghostfolio é uma poderosa ferramenta de gestão de portfólio que oferece aos investidores uma plataforma abrangente para monitorar, analisar e otimizar seus investimentos. Seja você um investidor experiente ou iniciante, o Ghostfolio oferece uma interface de usuário intuitiva e um ampla gama de funcionalidades para ajudá-lo a tomar decisões informadas e assumir o controle do seu futuro financeiro. + Você está procurando uma alternativa de código aberto para ? Ghostfolio é uma poderosa ferramenta de gestão de portfólio que oferece aos investidores uma plataforma abrangente para monitorar, analisar e otimizar seus investimentos. Seja você um investidor experiente ou iniciante, o Ghostfolio oferece uma interface de usuário intuitiva e um ampla gama de funcionalidades para ajudá-lo a tomar decisões informadas e assumir o controle do seu futuro financeiro. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -5480,7 +5480,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio é um software de código aberto (OSS), que oferece uma alternativa econômica para tornando-o particularmente adequado para indivíduos com orçamento apertado, como aqueles buscando Independência Financeira, Aposentadoria Antecipada (FIRE). Ao aproveitar os esforços coletivos de uma comunidade de desenvolvedores e entusiastas de finanças pessoais, o Ghostfolio aprimora continuamente seus recursos, segurança e experiência do usuário. + Ghostfolio é um software de código aberto (OSS), que oferece uma alternativa econômica para tornando-o particularmente adequado para indivíduos com orçamento apertado, como aqueles buscando Independência Financeira, Aposentadoria Antecipada (FIRE). Ao aproveitar os esforços coletivos de uma comunidade de desenvolvedores e entusiastas de finanças pessoais, o Ghostfolio aprimora continuamente seus recursos, segurança e experiência do usuário. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -5488,7 +5488,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Vamos nos aprofundar nos detalhes do Ghostfolio vs tabela de comparação abaixo para obter uma compreensão completa de como o Ghostfolio se posiciona em relação a . Exploraremos vários aspectos, como recursos, privacidade de dados, preços e muito mais, permitindo que você faça uma escolha bem informada para suas necessidades pessoais. + Vamos nos aprofundar nos detalhes do Ghostfolio vs tabela de comparação abaixo para obter uma compreensão completa de como o Ghostfolio se posiciona em relação a . Exploraremos vários aspectos, como recursos, privacidade de dados, preços e muito mais, permitindo que você faça uma escolha bem informada para suas necessidades pessoais. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5509,7 +5509,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Observe que as informações fornecidas no Ghostfolio vs. A tabela de comparação é baseada em nossa pesquisa e análise independentes. Este site não é afiliado a ou qualquer outro produto mencionado na comparação. À medida que o cenário das ferramentas de finanças pessoais evolui, é essencial verificar quaisquer detalhes ou alterações específicas diretamente na página do produto correspondente. Os dados precisam de uma atualização? Ajude-nos a manter dados precisos sobre GitHub. + Observe que as informações fornecidas no Ghostfolio vs. A tabela de comparação é baseada em nossa pesquisa e análise independentes. Este site não é afiliado a ou qualquer outro produto mencionado na comparação. À medida que o cenário das ferramentas de finanças pessoais evolui, é essencial verificar quaisquer detalhes ou alterações específicas diretamente na página do produto correspondente. Os dados precisam de uma atualização? Ajude-nos a manter dados precisos sobre GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5833,7 +5833,7 @@ Ghostfolio vs comparison table - Ghostfolio vs tabela de comparação + Ghostfolio vs tabela de comparação apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -7014,7 +7014,7 @@ is Open Source Software - é software de código aberto + é software de código aberto apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7026,7 +7026,7 @@ is not Open Source Software - não é software de código aberto + não é software de código aberto apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7062,7 +7062,7 @@ can be self-hosted - pode ser auto-hospedado + pode ser auto-hospedado apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7082,7 +7082,7 @@ cannot be self-hosted - não pode ser auto-hospedado + não pode ser auto-hospedado apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7094,7 +7094,7 @@ can be used anonymously - pode ser usado anonimamente + pode ser usado anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7106,7 +7106,7 @@ cannot be used anonymously - não pode ser usado anonimamente + não pode ser usado anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7118,7 +7118,7 @@ offers a free plan - oferece um plano gratuito + oferece um plano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7130,7 +7130,7 @@ does not offer a free plan - não oferece um plano gratuito + não oferece um plano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 9a3ed7acf..40e9f40a3 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -4221,7 +4221,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Açık kaynaklı bir alternatif mi arıyorsunuz ? Ghostfolio güçlü bir portföy yönetim aracıdır ve bireylere yatırımlarını takip etmek, analiz etmek ve optimize etmek için kapsamlı bir platform sunar. İster deneyimli bir yatırımcı olun ister yeni başlıyor olun, Ghostfolio, bilinçli kararlar almanıza ve finansal geleceğinizi kontrol etmenize yardımcı olmak için sezgisel bir kullanıcı arayüzü ve geniş bir işlevsellik yelpazesi sunmaktadır. + Açık kaynaklı bir alternatif mi arıyorsunuz ? Ghostfolio güçlü bir portföy yönetim aracıdır ve bireylere yatırımlarını takip etmek, analiz etmek ve optimize etmek için kapsamlı bir platform sunar. İster deneyimli bir yatırımcı olun ister yeni başlıyor olun, Ghostfolio, bilinçli kararlar almanıza ve finansal geleceğinizi kontrol etmenize yardımcı olmak için sezgisel bir kullanıcı arayüzü ve geniş bir işlevsellik yelpazesi sunmaktadır. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -4229,7 +4229,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio, karşısında açık kaynak kodlu maliyet etkin bir seçenek sunmaktadır. gibi kısıtlı bütçeye sahip, finansal özgürlük ve erken emeklilik (FIRE) amaçlayan kulanıcılar için özellikle uygundur. Ghostfolio, topluluk içindeki geliştiricilerin ve kişisel finans meraklılarının kolektif çabası sayesinde yeteneklerini, güvenliğini ve kullanıcı deneyimini sürekli olarak geliştirmektedir. + Ghostfolio, karşısında açık kaynak kodlu maliyet etkin bir seçenek sunmaktadır. gibi kısıtlı bütçeye sahip, finansal özgürlük ve erken emeklilik (FIRE) amaçlayan kulanıcılar için özellikle uygundur. Ghostfolio, topluluk içindeki geliştiricilerin ve kişisel finans meraklılarının kolektif çabası sayesinde yeteneklerini, güvenliğini ve kullanıcı deneyimini sürekli olarak geliştirmektedir. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -4237,7 +4237,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Ghostfolio ve arasındaki ayrıntılı karşılaştırmanın yer aldığı aşağıdaki tabloya daha yakından bakarak Ghostfolio’nun karşısında kendisini nasıl konumlandırdığını kapsamlı bir şekilde değerlendirelim. Bu kapsamda özellikler, veri güvenliği, fiyat vb. hususları inceleyerek kişisel gereksinimleriniz için bilgiye dayalı bir seçim yapmanızı sağlayabileceği. + Ghostfolio ve arasındaki ayrıntılı karşılaştırmanın yer aldığı aşağıdaki tabloya daha yakından bakarak Ghostfolio’nun karşısında kendisini nasıl konumlandırdığını kapsamlı bir şekilde değerlendirelim. Bu kapsamda özellikler, veri güvenliği, fiyat vb. hususları inceleyerek kişisel gereksinimleriniz için bilgiye dayalı bir seçim yapmanızı sağlayabileceği. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4389,7 +4389,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Lütfen dikkat, Ghostfolio ve arasındaki karşılaştırmanın yer aldığı tablodaki bilgiler bağımsız araştırmalarımıza dayanmaktadır. Websitemizin ile ya da bu karşılaştırmada adı geçen herhangi bir ürün ve hizmet ile ilişkisi bulunmamaktadır. Kişisel finans araçlarının kapsamı geliştikçe, belirli ayrıntıların veya değişikliklerin doğrudan ilgili ürün sayfasından doğrulanması önemlidir. Verilerin yenilenmesi mi gerekiyor? Doğru verileri sağlamamıza yardımcı olmak için sayfamızı ziyaret edin. GitHub. + Lütfen dikkat, Ghostfolio ve arasındaki karşılaştırmanın yer aldığı tablodaki bilgiler bağımsız araştırmalarımıza dayanmaktadır. Websitemizin ile ya da bu karşılaştırmada adı geçen herhangi bir ürün ve hizmet ile ilişkisi bulunmamaktadır. Kişisel finans araçlarının kapsamı geliştikçe, belirli ayrıntıların veya değişikliklerin doğrudan ilgili ürün sayfasından doğrulanması önemlidir. Verilerin yenilenmesi mi gerekiyor? Doğru verileri sağlamamıza yardımcı olmak için sayfamızı ziyaret edin. GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -5833,7 +5833,7 @@ Ghostfolio vs comparison table - Ghostfolio ve karşılatırma tablosu + Ghostfolio ve karşılatırma tablosu apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -7014,7 +7014,7 @@ is Open Source Software - , Açık Kaynak Kodlu Yazılımdır + , Açık Kaynak Kodlu Yazılımdır apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7026,7 +7026,7 @@ is not Open Source Software - , Açık Kaynak Kodlu Yazılımdır + , Açık Kaynak Kodlu Yazılımdır apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7062,7 +7062,7 @@ can be self-hosted - kendi sunucunuzda barındırılabilir + kendi sunucunuzda barındırılabilir apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7082,7 +7082,7 @@ cannot be self-hosted - kendi sunucunuzda barındırılmaz + kendi sunucunuzda barındırılmaz apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7094,7 +7094,7 @@ can be used anonymously - gizli kullanımda kullanılabilir + gizli kullanımda kullanılabilir apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7106,7 +7106,7 @@ cannot be used anonymously - gizli kullanımda kullanılmaz + gizli kullanımda kullanılmaz apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7118,7 +7118,7 @@ offers a free plan - ücretsiz plan sunar + ücretsiz plan sunar apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7130,7 +7130,7 @@ does not offer a free plan - ücretsiz plan sunmaz + ücretsiz plan sunmaz apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index 422191958..5831e40be 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -5828,7 +5828,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - Ви шукаєте відкриту альтернативу для ? Ghostfolio є потужним інструментом управління портфелем, який надає індивідуумам комплексну платформу для відстеження, аналізу та оптимізації їхніх інвестицій. Незалежно від того, чи ви досвідчений інвестор, чи тільки починаєте, Ghostfolio пропонує зручний інтерфейс користувача та широкий спектр функціональностей для допомоги вам у прийнятті обґрунтованих рішень та взятті під контроль вашого фінансового майбутнього. + Ви шукаєте відкриту альтернативу для ? Ghostfolio є потужним інструментом управління портфелем, який надає індивідуумам комплексну платформу для відстеження, аналізу та оптимізації їхніх інвестицій. Незалежно від того, чи ви досвідчений інвестор, чи тільки починаєте, Ghostfolio пропонує зручний інтерфейс користувача та широкий спектр функціональностей для допомоги вам у прийнятті обґрунтованих рішень та взятті під контроль вашого фінансового майбутнього. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -5836,7 +5836,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio є відкритим програмним забезпеченням (OSS), що надає економічно ефективну альтернативу для , роблячи його особливо підходящим для тих, хто обмежений у бюджеті, таких як прагнення до фінансової незалежності, раннього виходу на пенсію (FIRE). Використовуючи колективні зусилля спільноти розробників та ентузіастів особистих фінансів, Ghostfolio постійно покращує свої можливості, безпеку та користувацький досвід. + Ghostfolio є відкритим програмним забезпеченням (OSS), що надає економічно ефективну альтернативу для , роблячи його особливо підходящим для тих, хто обмежений у бюджеті, таких як прагнення до фінансової незалежності, раннього виходу на пенсію (FIRE). Використовуючи колективні зусилля спільноти розробників та ентузіастів особистих фінансів, Ghostfolio постійно покращує свої можливості, безпеку та користувацький досвід. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -5844,7 +5844,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - Давайте заглибимося у детальну порівняльну таблицю Ghostfolio проти нижче, щоб отримати повне розуміння того, як Ghostfolio позиціонує себе відносно . Ми дослідимо різні аспекти, такі як функції, конфіденційність даних, ціни тощо, що дозволить вам зробити добре обдуманий вибір для ваших особистих потреб. + Давайте заглибимося у детальну порівняльну таблицю Ghostfolio проти нижче, щоб отримати повне розуміння того, як Ghostfolio позиціонує себе відносно . Ми дослідимо різні аспекти, такі як функції, конфіденційність даних, ціни тощо, що дозволить вам зробити добре обдуманий вибір для ваших особистих потреб. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -5864,7 +5864,7 @@ Ghostfolio vs comparison table - Порівняльна таблиця Ghostfolio проти + Порівняльна таблиця Ghostfolio проти apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -5912,7 +5912,7 @@ is Open Source Software - є відкритим програмним забезпеченням + є відкритим програмним забезпеченням apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -5960,7 +5960,7 @@ is not Open Source Software - не є відкритим програмним забезпеченням + не є відкритим програмним забезпеченням apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -6040,7 +6040,7 @@ can be self-hosted - може бути self-hosted + може бути self-hosted apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -6060,7 +6060,7 @@ cannot be self-hosted - не може бути self-hosted + не може бути self-hosted apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -6080,7 +6080,7 @@ can be used anonymously - може використовуватися анонімно + може використовуватися анонімно apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -6092,7 +6092,7 @@ cannot be used anonymously - не може використовуватися анонімно + не може використовуватися анонімно apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -6112,7 +6112,7 @@ offers a free plan - пропонує безкоштовний план + пропонує безкоштовний план apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -6124,7 +6124,7 @@ does not offer a free plan - не пропонує безкоштовний план + не пропонує безкоштовний план apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 @@ -6164,7 +6164,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - Зазначаємо, що інформація, надана в порівняльній таблиці Ghostfolio проти базується на нашому незалежному дослідженні та аналізі. Цей веб-сайт не пов’язаний з або будь-яким іншим продуктом, згаданим у порівнянні. Оскільки ландшафт інструментів особистих фінансів еволюціонує, важливо перевіряти будь-які конкретні деталі або зміни безпосередньо на сторінці відповідного продукту. Потрібно оновити дані? Допоможіть нам підтримувати точні дані на GitHub. + Зазначаємо, що інформація, надана в порівняльній таблиці Ghostfolio проти базується на нашому незалежному дослідженні та аналізі. Цей веб-сайт не пов’язаний з або будь-яким іншим продуктом, згаданим у порівнянні. Оскільки ландшафт інструментів особистих фінансів еволюціонує, важливо перевіряти будь-які конкретні деталі або зміни безпосередньо на сторінці відповідного продукту. Потрібно оновити дані? Допоможіть нам підтримувати точні дані на GitHub. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index a0d0381ee..15815382f 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -4778,7 +4778,7 @@ Are you looking for an open source alternative to ? Ghostfolio is a powerful portfolio management tool that provides individuals with a comprehensive platform to track, analyze, and optimize their investments. Whether you are an experienced investor or just starting out, Ghostfolio offers an intuitive user interface and a wide range of functionalities to help you make informed decisions and take control of your financial future. - 您是否正在寻找开源替代方案幽灵作品集是一个强大的投资组合管理工具,为个人提供一个全面的平台来跟踪、分析和优化他们的投资。无论您是经验丰富的投资者还是刚刚起步的投资者,Ghostfolio 都提供直观的用户界面和广泛的功能帮助您做出明智的决定并掌控您的财务未来。 + 您是否正在寻找开源替代方案幽灵作品集是一个强大的投资组合管理工具,为个人提供一个全面的平台来跟踪、分析和优化他们的投资。无论您是经验丰富的投资者还是刚刚起步的投资者,Ghostfolio 都提供直观的用户界面和广泛的功能帮助您做出明智的决定并掌控您的财务未来。 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 19 @@ -4786,7 +4786,7 @@ Ghostfolio is an open source software (OSS), providing a cost-effective alternative to making it particularly suitable for individuals on a tight budget, such as those pursuing Financial Independence, Retire Early (FIRE). By leveraging the collective efforts of a community of developers and personal finance enthusiasts, Ghostfolio continuously enhances its capabilities, security, and user experience. - Ghostfolio 是一款开源软件 (OSS),提供了一种经济高效的替代方案使其特别适合预算紧张的个人,例如追求财务独立,提前退休(FIRE) 。通过利用开发者社区和个人理财爱好者的集体努力,Ghostfolio 不断增强其功能、安全性和用户体验。 + Ghostfolio 是一款开源软件 (OSS),提供了一种经济高效的替代方案使其特别适合预算紧张的个人,例如追求财务独立,提前退休(FIRE) 。通过利用开发者社区和个人理财爱好者的集体努力,Ghostfolio 不断增强其功能、安全性和用户体验。 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 33 @@ -4794,7 +4794,7 @@ Let’s dive deeper into the detailed Ghostfolio vs comparison table below to gain a thorough understanding of how Ghostfolio positions itself relative to . We will explore various aspects such as features, data privacy, pricing, and more, allowing you to make a well-informed choice for your personal requirements. - 让我们更深入地了解 Ghostfolio 与下面的比较表可帮助您全面了解 Ghostfolio 相对于其他产品的定位。我们将探讨功能、数据隐私、定价等各个方面,让您根据个人需求做出明智的选择。 + 让我们更深入地了解 Ghostfolio 与下面的比较表可帮助您全面了解 Ghostfolio 相对于其他产品的定位。我们将探讨功能、数据隐私、定价等各个方面,让您根据个人需求做出明智的选择。 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 44 @@ -4814,7 +4814,7 @@ Ghostfolio vs comparison table - Ghostfolio vs比较表 + Ghostfolio vs比较表 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 55 @@ -4978,7 +4978,7 @@ Please note that the information provided in the Ghostfolio vs comparison table is based on our independent research and analysis. This website is not affiliated with or any other product mentioned in the comparison. As the landscape of personal finance tools evolves, it is essential to verify any specific details or changes directly from the respective product page. Data needs a refresh? Help us maintain accurate data on GitHub. - 请注意,在 Ghostfolio 与的比较表中提供的信息基于我们的独立研究和分析。该网站不隶属于或比较中提到的任何其他产品。随着个人理财工具格局的不断发展,直接从相应的产品页面验证任何具体的细节或变化至关重要。数据需要刷新吗?帮助我们在GitHub 上维护准确的数据。 + 请注意,在 Ghostfolio 与的比较表中提供的信息基于我们的独立研究和分析。该网站不隶属于或比较中提到的任何其他产品。随着个人理财工具格局的不断发展,直接从相应的产品页面验证任何具体的细节或变化至关重要。数据需要刷新吗?帮助我们在GitHub 上维护准确的数据。 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 312 @@ -7015,7 +7015,7 @@ is Open Source Software - 是开源软件 + 是开源软件 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7027,7 +7027,7 @@ is not Open Source Software - 不是开源软件 + 不是开源软件 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7063,7 +7063,7 @@ can be self-hosted - 可以自托管 + 可以自托管 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7083,7 +7083,7 @@ cannot be self-hosted - 无法自托管 + 无法自托管 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7095,7 +7095,7 @@ can be used anonymously - 可以匿名使用 + 可以匿名使用 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7107,7 +7107,7 @@ cannot be used anonymously - 无法匿名使用 + 无法匿名使用 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7119,7 +7119,7 @@ offers a free plan - 提供免费计划 + 提供免费计划 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7131,7 +7131,7 @@ does not offer a free plan - 不提供免费计划 + 不提供免费计划 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 From 242f30f6caea0d80764e1f610dcea61d17bc9447 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Tue, 23 Jun 2026 22:05:16 +0200 Subject: [PATCH 33/60] Release 3.15.0 (#7117) --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63ac12cb9..f175878fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 3.15.0 - 2026-06-23 ### Changed diff --git a/package-lock.json b/package-lock.json index 14bc7c595..d3250d5d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "3.14.0", + "version": "3.15.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "3.14.0", + "version": "3.15.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 6b7c5a1c2..423b64cb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "3.14.0", + "version": "3.15.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From 2b5d2701bd00acb4fff83e1ebf2f5b19b42dfeb9 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 00:06:01 +0200 Subject: [PATCH 34/60] Task/improve exchange rate and market data gathering robustness (#7119) * Improve exchange rate robustness * Improve market data gathering robustness * Update changelog --- CHANGELOG.md | 2 + .../src/app/activities/activities.service.ts | 8 +- apps/api/src/app/import/import.service.ts | 15 ++-- .../src/app/portfolio/portfolio.service.ts | 8 +- .../market-data/market-data.service.ts | 74 ++++++++++--------- libs/common/src/lib/config.ts | 1 + 6 files changed, 58 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f175878fc..c8d6a7244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an issue where symbols with special characters caused API request failures by URL encoding the symbol - Fixed the disabled state of the delete action in the asset profiles actions menu of the historical market data table in the admin control panel - Fixed the persistence of an empty `locale` string in the scraper configuration +- Fixed a transaction timeout that prevented gathering historical market data for symbols with a long history +- Fixed an exception in various portfolio endpoints when historical exchange rate data is missing ## 3.14.0 - 2026-06-22 diff --git a/apps/api/src/app/activities/activities.service.ts b/apps/api/src/app/activities/activities.service.ts index f57507e3d..44ab67e45 100644 --- a/apps/api/src/app/activities/activities.service.ts +++ b/apps/api/src/app/activities/activities.service.ts @@ -746,10 +746,10 @@ export class ActivitiesService { const value = new Big(order.quantity).mul(order.unitPrice).toNumber(); const [ - feeInAssetProfileCurrency, - feeInBaseCurrency, - unitPriceInAssetProfileCurrency, - valueInBaseCurrency + feeInAssetProfileCurrency = 0, + feeInBaseCurrency = 0, + unitPriceInAssetProfileCurrency = 0, + valueInBaseCurrency = 0 ] = await Promise.all([ this.exchangeRateDataService.toCurrencyAtDate( order.fee, diff --git a/apps/api/src/app/import/import.service.ts b/apps/api/src/app/import/import.service.ts index 2ecc4d3a5..ba704d5ad 100644 --- a/apps/api/src/app/import/import.service.ts +++ b/apps/api/src/app/import/import.service.ts @@ -592,18 +592,19 @@ export class ImportService { const value = new Big(quantity).mul(unitPrice).toNumber(); - const valueInBaseCurrency = this.exchangeRateDataService.toCurrencyAtDate( - value, - currency ?? assetProfile.currency, - userCurrency, - date - ); + const valueInBaseCurrency = + (await this.exchangeRateDataService.toCurrencyAtDate( + value, + currency ?? assetProfile.currency, + userCurrency, + date + )) ?? 0; activities.push({ ...order, error, value, - valueInBaseCurrency: await valueInBaseCurrency, + valueInBaseCurrency, // @ts-ignore SymbolProfile: assetProfile }); diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 418e60401..77cbb5b49 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -205,21 +205,21 @@ export class PortfolioService { switch (type) { case ActivityType.DIVIDEND: dividendInBaseCurrency += - await this.exchangeRateDataService.toCurrencyAtDate( + (await this.exchangeRateDataService.toCurrencyAtDate( new Big(quantity).mul(unitPrice).toNumber(), currency ?? SymbolProfile.currency, userCurrency, date - ); + )) ?? 0; break; case ActivityType.INTEREST: interestInBaseCurrency += - await this.exchangeRateDataService.toCurrencyAtDate( + (await this.exchangeRateDataService.toCurrencyAtDate( unitPrice, currency ?? SymbolProfile.currency, userCurrency, date - ); + )) ?? 0; break; } diff --git a/apps/api/src/services/market-data/market-data.service.ts b/apps/api/src/services/market-data/market-data.service.ts index 27c741055..086434724 100644 --- a/apps/api/src/services/market-data/market-data.service.ts +++ b/apps/api/src/services/market-data/market-data.service.ts @@ -1,6 +1,7 @@ import { DateQuery } from '@ghostfolio/api/app/portfolio/interfaces/date-query.interface'; import { DataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; +import { DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_TIMEOUT } from '@ghostfolio/common/config'; import { UpdateMarketDataDto } from '@ghostfolio/common/dtos'; import { resetHours } from '@ghostfolio/common/helper'; import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; @@ -155,49 +156,52 @@ export class MarketDataService { dataSource, symbol }: AssetProfileIdentifier & { data: Prisma.MarketDataUpdateInput[] }) { - await this.prismaService.$transaction(async (prisma) => { - if (data.length > 0) { - let minTime = Infinity; - let maxTime = -Infinity; + await this.prismaService.$transaction( + async (prisma) => { + if (data.length > 0) { + let minTime = Infinity; + let maxTime = -Infinity; - for (const { date } of data) { - const time = (date as Date).getTime(); + for (const { date } of data) { + const time = (date as Date).getTime(); - if (time < minTime) { - minTime = time; - } + if (time < minTime) { + minTime = time; + } - if (time > maxTime) { - maxTime = time; + if (time > maxTime) { + maxTime = time; + } } - } - const minDate = new Date(minTime); - const maxDate = new Date(maxTime); + const minDate = new Date(minTime); + const maxDate = new Date(maxTime); - await prisma.marketData.deleteMany({ - where: { - dataSource, - symbol, - date: { - gte: minDate, - lte: maxDate + await prisma.marketData.deleteMany({ + where: { + dataSource, + symbol, + date: { + gte: minDate, + lte: maxDate + } } - } - }); + }); - await prisma.marketData.createMany({ - data: data.map(({ date, marketPrice, state }) => ({ - dataSource, - symbol, - date: date as Date, - marketPrice: marketPrice as number, - state: state as MarketDataState - })), - skipDuplicates: true - }); - } - }); + await prisma.marketData.createMany({ + data: data.map(({ date, marketPrice, state }) => ({ + dataSource, + symbol, + date: date as Date, + marketPrice: marketPrice as number, + state: state as MarketDataState + })), + skipDuplicates: true + }); + } + }, + { timeout: DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_TIMEOUT } + ); } public async updateAssetProfileIdentifier( diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index e6d717c7b..f96a934e7 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -90,6 +90,7 @@ export const DEFAULT_PAGE_SIZE = 50; export const DEFAULT_PORT = 3333; export const DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY = 1; +export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_TIMEOUT = 60000; export const DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = 30000; From 5cd81192ee09e83f4f64ab2298ce9cff4e6faeea Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 00:07:34 +0200 Subject: [PATCH 35/60] Release 3.15.1 (#7120) --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8d6a7244..a903122d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## 3.15.0 - 2026-06-23 +## 3.15.1 - 2026-06-23 ### Changed diff --git a/package-lock.json b/package-lock.json index d3250d5d0..e40bcbe1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "3.15.0", + "version": "3.15.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "3.15.0", + "version": "3.15.1", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 423b64cb9..df644f017 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "3.15.0", + "version": "3.15.1", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From 020c982e73c706c7ec0a81fabe75ef0c73dad7fe Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:05:11 +0200 Subject: [PATCH 36/60] Bugfix/hourly market data updates not refreshing prices for asset profiles with manual data source (#7122) * Fix hourly market data updates not refreshing prices for MANUAL asset profiles * Update changelog --- CHANGELOG.md | 6 +++ .../data-provider/data-provider.service.ts | 6 ++- .../interfaces/data-provider.interface.ts | 1 + .../data-provider/manual/manual.service.ts | 45 ++++++++++--------- 4 files changed, 35 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a903122d4..4ec1539da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Fixed an issue with hourly market data updates not refreshing prices for asset profiles with `MANUAL` data source + ## 3.15.1 - 2026-06-23 ### Changed diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 5d49848fa..10b0e6fd8 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -662,7 +662,11 @@ export class DataProviderService implements OnModuleInit { ); const promise = Promise.resolve( - dataProvider.getQuotes({ requestTimeout, symbols: symbolsChunk }) + dataProvider.getQuotes({ + requestTimeout, + useCache, + symbols: symbolsChunk + }) ); promises.push( diff --git a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts index a55c9f328..5002fa87e 100644 --- a/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts +++ b/apps/api/src/services/data-provider/interfaces/data-provider.interface.ts @@ -75,6 +75,7 @@ export interface GetHistoricalParams { export interface GetQuotesParams { requestTimeout?: number; symbols: string[]; + useCache?: boolean; } export interface GetSearchParams { diff --git a/apps/api/src/services/data-provider/manual/manual.service.ts b/apps/api/src/services/data-provider/manual/manual.service.ts index 87e116dda..66571c239 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -136,7 +136,8 @@ export class ManualService implements DataProviderInterface { } public async getQuotes({ - symbols + symbols, + useCache = true }: GetQuotesParams): Promise<{ [symbol: string]: DataProviderResponse }> { const response: { [symbol: string]: DataProviderResponse } = {}; @@ -164,32 +165,32 @@ export class ManualService implements DataProviderInterface { } }); - const symbolProfilesWithScraperConfigurationAndInstantMode = - symbolProfiles.filter(({ scraperConfiguration }) => { + const symbolProfilesToScrape = symbolProfiles.filter( + ({ scraperConfiguration }) => { return ( - scraperConfiguration?.mode === 'instant' && + (scraperConfiguration?.mode === 'instant' || !useCache) && scraperConfiguration?.selector && scraperConfiguration?.url ); - }); - - const scraperResultPromises = - symbolProfilesWithScraperConfigurationAndInstantMode.map( - async ({ scraperConfiguration, symbol }) => { - try { - const marketPrice = await this.scrape({ - scraperConfiguration, - symbol - }); - return { marketPrice, symbol }; - } catch (error) { - this.logger.error( - `Could not get quote for ${symbol} (${this.getName()}): [${error.name}] ${error.message}` - ); - return { symbol, marketPrice: undefined }; - } + } + ); + + const scraperResultPromises = symbolProfilesToScrape.map( + async ({ scraperConfiguration, symbol }) => { + try { + const marketPrice = await this.scrape({ + scraperConfiguration, + symbol + }); + return { marketPrice, symbol }; + } catch (error) { + this.logger.error( + `Could not get quote for ${symbol} (${this.getName()}): [${error.name}] ${error.message}` + ); + return { symbol, marketPrice: undefined }; } - ); + } + ); // Wait for all scraping requests to complete concurrently const scraperResults = await Promise.all(scraperResultPromises); From 11f37f8f7ddae1eedd91c361ea8d5d7f697249ba Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:06:26 +0200 Subject: [PATCH 37/60] Task/improve style of Ghostfolio Premium card in admin settings (#7127) Improve style --- .../admin-settings.component.scss | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/apps/client/src/app/components/admin-settings/admin-settings.component.scss b/apps/client/src/app/components/admin-settings/admin-settings.component.scss index 1c0a17624..29340ea0d 100644 --- a/apps/client/src/app/components/admin-settings/admin-settings.component.scss +++ b/apps/client/src/app/components/admin-settings/admin-settings.component.scss @@ -10,7 +10,33 @@ } .mat-mdc-card { - --mat-card-outlined-container-color: whitesmoke; + --gradient-opacity: 50%; + --mat-card-outlined-outline-color: rgb(193, 219, 254); + + background-color: white; + background-image: linear-gradient( + 109.6deg, + color-mix(in srgb, rgba(255, 255, 255, 1) 100%, transparent) 11.2%, + color-mix( + in srgb, + rgba(221, 108, 241, 0.26) var(--gradient-opacity), + transparent + ) + 42%, + color-mix( + in srgb, + rgba(229, 106, 253, 0.71) var(--gradient-opacity), + transparent + ) + 71.5%, + color-mix( + in srgb, + rgba(123, 183, 253, 1) var(--gradient-opacity), + transparent + ) + 100% + ); + color: rgb(var(--dark-primary-text)); .mat-mdc-card-actions { min-height: 0; @@ -32,6 +58,6 @@ :host-context(.theme-dark) { .mat-mdc-card { - --mat-card-outlined-container-color: #222222; + --mat-card-outlined-outline-color: white; } } From a2dbfcf8e301cd8b4ed5a70c13ba019d536820e9 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:07:04 +0200 Subject: [PATCH 38/60] Task/refactor timeouts to use ms() (#7123) Refactor to use ms() --- libs/common/src/lib/config.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index f96a934e7..03600088f 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -90,10 +90,12 @@ export const DEFAULT_PAGE_SIZE = 50; export const DEFAULT_PORT = 3333; export const DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY = 1; -export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_TIMEOUT = 60000; +export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_TIMEOUT = + ms('1 minute'); export const DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY = 1; -export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = 30000; +export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = + ms('30 seconds'); export const DEFAULT_REDACTED_PATHS = [ 'accounts[*].balance', From f64edb6705aa86cedb968567603fc669f1cb9371 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:16:54 +0200 Subject: [PATCH 39/60] Feature/add pagination to platform and tag management of admin control panel (#7126) * Add pagination * Update changelog --- CHANGELOG.md | 5 +++++ .../components/admin-platform/admin-platform.component.html | 6 ++++++ .../components/admin-platform/admin-platform.component.ts | 6 ++++++ .../src/app/components/admin-tag/admin-tag.component.html | 6 ++++++ .../src/app/components/admin-tag/admin-tag.component.ts | 6 ++++++ 5 files changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec1539da..47c43d38f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added pagination to the platform management of the admin control panel +- Added pagination to the tag management of the admin control panel + ### Fixed - Fixed an issue with hourly market data updates not refreshing prices for asset profiles with `MANUAL` data source diff --git a/apps/client/src/app/components/admin-platform/admin-platform.component.html b/apps/client/src/app/components/admin-platform/admin-platform.component.html index 44f5a6eab..19682bdc0 100644 --- a/apps/client/src/app/components/admin-platform/admin-platform.component.html +++ b/apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -96,3 +96,9 @@
- Created + Creation - + @if (element.price === null) { + + } @else { + + } - Expires + Expiration Date: Sun, 21 Jun 2026 09:16:51 +0200 Subject: [PATCH 06/60] Task/reuse asset profile identifier (#7086) Reuse AssetProfileIdentifier --- apps/client/src/app/app.component.ts | 12 ++++++------ .../admin-market-data/admin-market-data.component.ts | 5 +---- libs/ui/src/lib/services/data.service.ts | 10 ++-------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts index e1967970d..90ff2f1bc 100644 --- a/apps/client/src/app/app.component.ts +++ b/apps/client/src/app/app.component.ts @@ -1,5 +1,9 @@ import { getCssVariable } from '@ghostfolio/common/helper'; -import { InfoItem, User } from '@ghostfolio/common/interfaces'; +import { + AssetProfileIdentifier, + InfoItem, + User +} from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { internalRoutes, publicRoutes } from '@ghostfolio/common/routes/routes'; import { ColorScheme } from '@ghostfolio/common/types'; @@ -27,7 +31,6 @@ import { RouterLink, RouterOutlet } from '@angular/router'; -import { DataSource } from '@prisma/client'; import { Chart } from 'chart.js'; import { addIcons } from 'ionicons'; import { openOutline } from 'ionicons/icons'; @@ -269,10 +272,7 @@ export class GfAppComponent implements OnInit { private openHoldingDetailDialog({ dataSource, symbol - }: { - dataSource: DataSource; - symbol: string; - }) { + }: AssetProfileIdentifier) { this.userService .get() .pipe(takeUntilDestroyed(this.destroyRef)) diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index f7396eb1d..511d6df98 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -408,10 +408,7 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit { private openAssetProfileDialog({ dataSource, symbol - }: { - dataSource: DataSource; - symbol: string; - }) { + }: AssetProfileIdentifier) { this.userService .get() .pipe(takeUntilDestroyed(this.destroyRef)) diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index ddd5f30b2..2e0e212bc 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -479,10 +479,7 @@ export class DataService { public fetchHoldingDetail({ dataSource, symbol - }: { - dataSource: DataSource; - symbol: string; - }): Observable< + }: AssetProfileIdentifier): Observable< Omit & { dateOfFirstActivity: Date | undefined; } @@ -541,10 +538,7 @@ export class DataService { public fetchMarketDataBySymbol({ dataSource, symbol - }: { - dataSource: DataSource; - symbol: string; - }): Observable { + }: AssetProfileIdentifier): Observable { return this.http .get(`/api/v1/asset-profiles/${dataSource}/${symbol}`) .pipe( From 2f96b4210755821bc3bae29c1481578746c7ebb0 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 21 Jun 2026 09:18:43 +0200 Subject: [PATCH 07/60] Task/reuse asset profile identifier (part 2) (#7087) Reuse AssetProfileIdentifier --- .../endpoints/watchlist/watchlist.service.ts | 19 +++++++------------ .../app/portfolio/portfolio.service.spec.ts | 3 ++- .../src/app/portfolio/portfolio.service.ts | 9 +++------ .../events/asset-profile-changed.listener.ts | 8 ++------ .../data-provider/data-provider.service.ts | 4 +--- .../data-gathering/data-gathering.service.ts | 8 ++------ .../responses/export-response.interface.ts | 12 +++--------- libs/ui/src/lib/services/admin.service.ts | 8 ++------ libs/ui/src/lib/services/data.service.ts | 6 +----- 9 files changed, 23 insertions(+), 54 deletions(-) diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts index 791cc6c69..78786c00b 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts @@ -5,10 +5,13 @@ import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; -import { WatchlistResponse } from '@ghostfolio/common/interfaces'; +import { + AssetProfileIdentifier, + WatchlistResponse +} from '@ghostfolio/common/interfaces'; import { BadRequestException, Injectable } from '@nestjs/common'; -import { DataSource, Prisma } from '@prisma/client'; +import { Prisma } from '@prisma/client'; @Injectable() export class WatchlistService { @@ -25,11 +28,7 @@ export class WatchlistService { dataSource, symbol, userId - }: { - dataSource: DataSource; - symbol: string; - userId: string; - }): Promise { + }: { userId: string } & AssetProfileIdentifier): Promise { const symbolProfile = await this.prismaService.symbolProfile.findUnique({ where: { dataSource_symbol: { dataSource, symbol } @@ -73,11 +72,7 @@ export class WatchlistService { dataSource, symbol, userId - }: { - dataSource: DataSource; - symbol: string; - userId: string; - }) { + }: { userId: string } & AssetProfileIdentifier) { await this.prismaService.user.update({ data: { watchlist: { diff --git a/apps/api/src/app/portfolio/portfolio.service.spec.ts b/apps/api/src/app/portfolio/portfolio.service.spec.ts index e0e7a8255..04142c92b 100644 --- a/apps/api/src/app/portfolio/portfolio.service.spec.ts +++ b/apps/api/src/app/portfolio/portfolio.service.spec.ts @@ -11,6 +11,7 @@ import { ImpersonationService } from '@ghostfolio/api/services/impersonation/imp import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { UNKNOWN_KEY } from '@ghostfolio/common/config'; import { parseDate } from '@ghostfolio/common/helper'; +import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { Account, DataSource } from '@prisma/client'; import { Big } from 'big.js'; @@ -198,7 +199,7 @@ describe('PortfolioService', () => { portfolioService as unknown as { getCashSymbolProfiles: ( aCashDetails: CashDetails - ) => { dataSource: DataSource; symbol: string }[]; + ) => AssetProfileIdentifier[]; } ).getCashSymbolProfiles(cashDetails); diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index c15353521..d6a54b180 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -46,6 +46,7 @@ import { import { AccountsResponse, Activity, + AssetProfileIdentifier, EnhancedSymbolProfile, Filter, HistoricalDataItem, @@ -776,11 +777,9 @@ export class PortfolioService { symbol, userId }: { - dataSource: DataSource; impersonationId: string; - symbol: string; userId: string; - }): Promise { + } & AssetProfileIdentifier): Promise { userId = await this.getUserId(impersonationId, userId); const user = await this.userService.user({ id: userId }); const userCurrency = this.getUserCurrency(user); @@ -1381,12 +1380,10 @@ export class PortfolioService { tags, userId }: { - dataSource: DataSource; impersonationId: string; - symbol: string; tags: Tag[]; userId: string; - }) { + } & AssetProfileIdentifier) { userId = await this.getUserId(impersonationId, userId); await this.activitiesService.assignTags({ diff --git a/apps/api/src/events/asset-profile-changed.listener.ts b/apps/api/src/events/asset-profile-changed.listener.ts index e2aea382e..15c19ff0a 100644 --- a/apps/api/src/events/asset-profile-changed.listener.ts +++ b/apps/api/src/events/asset-profile-changed.listener.ts @@ -5,10 +5,10 @@ import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate- import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; +import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; -import { DataSource } from '@prisma/client'; import ms from 'ms'; import { AssetProfileChangedEvent } from './asset-profile-changed.event'; @@ -64,11 +64,7 @@ export class AssetProfileChangedListener { currency, dataSource, symbol - }: { - currency: string; - dataSource: DataSource; - symbol: string; - }) { + }: { currency: string } & AssetProfileIdentifier) { this.logger.log(`Asset profile of ${symbol} (${dataSource}) has changed`); if ( diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index fb9efff44..6780f58f0 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -320,12 +320,10 @@ export class DataProviderService implements OnModuleInit { symbol, to }: { - dataSource: DataSource; from: Date; granularity: Granularity; - symbol: string; to: Date; - }) { + } & AssetProfileIdentifier) { return this.getDataProvider(DataSource[dataSource]).getDividends({ from, granularity, diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts index 789dea49b..aa6c952ef 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts @@ -28,7 +28,7 @@ import { import { InjectQueue } from '@nestjs/bull'; import { Inject, Injectable, Logger } from '@nestjs/common'; -import { DataSource, Prisma } from '@prisma/client'; +import { Prisma } from '@prisma/client'; import { JobOptions, Queue } from 'bull'; import { format, min, subDays, subMilliseconds, subYears } from 'date-fns'; import { isEmpty } from 'lodash'; @@ -122,11 +122,7 @@ export class DataGatheringService { dataSource, date, symbol - }: { - dataSource: DataSource; - date: Date; - symbol: string; - }) { + }: { date: Date } & AssetProfileIdentifier) { try { const historicalData = await this.dataProviderService.getHistoricalRaw({ assetProfileIdentifiers: [{ dataSource, symbol }], diff --git a/libs/common/src/lib/interfaces/responses/export-response.interface.ts b/libs/common/src/lib/interfaces/responses/export-response.interface.ts index 0e8ba1574..beffad7f1 100644 --- a/libs/common/src/lib/interfaces/responses/export-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/export-response.interface.ts @@ -1,13 +1,7 @@ -import { - Account, - DataSource, - Order, - Platform, - SymbolProfile, - Tag -} from '@prisma/client'; +import { Account, Order, Platform, SymbolProfile, Tag } from '@prisma/client'; import { AccountBalance } from '../account-balance.interface'; +import { AssetProfileIdentifier } from '../asset-profile-identifier.interface'; import { MarketData } from '../market-data.interface'; import { UserSettings } from '../user-settings.interface'; @@ -24,7 +18,7 @@ export interface ExportResponse { | 'symbolProfileId' | 'updatedAt' | 'userId' - > & { dataSource: DataSource; date: string; symbol: string })[]; + > & { date: string } & AssetProfileIdentifier)[]; assetProfiles: (Omit< SymbolProfile, | 'createdAt' diff --git a/libs/ui/src/lib/services/admin.service.ts b/libs/ui/src/lib/services/admin.service.ts index 90e477f28..acfd15ef5 100644 --- a/libs/ui/src/lib/services/admin.service.ts +++ b/libs/ui/src/lib/services/admin.service.ts @@ -23,7 +23,7 @@ import { GF_ENVIRONMENT } from '@ghostfolio/ui/environment'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable, inject } from '@angular/core'; -import { DataSource, MarketData, Platform } from '@prisma/client'; +import { MarketData, Platform } from '@prisma/client'; import { JobStatus } from 'bull'; import { isNumber } from 'lodash'; @@ -171,11 +171,7 @@ export class AdminService { dataSource, dateString, symbol - }: { - dataSource: DataSource; - dateString: string; - symbol: string; - }) { + }: { dateString: string } & AssetProfileIdentifier) { const url = `/api/v1/symbol/${dataSource}/${symbol}/${dateString}`; return this.http.get(url); diff --git a/libs/ui/src/lib/services/data.service.ts b/libs/ui/src/lib/services/data.service.ts index 2e0e212bc..8d16c6d35 100644 --- a/libs/ui/src/lib/services/data.service.ts +++ b/libs/ui/src/lib/services/data.service.ts @@ -850,11 +850,7 @@ export class DataService { dataSource, marketData, symbol - }: { - dataSource: DataSource; - marketData: UpdateBulkMarketDataDto; - symbol: string; - }) { + }: { marketData: UpdateBulkMarketDataDto } & AssetProfileIdentifier) { const url = `/api/v1/market-data/${dataSource}/${symbol}`; return this.http.post(url, marketData); From e0cae53c07ea7949550aeba287a1329f5675bf77 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2026 09:42:52 +0200 Subject: [PATCH 08/60] Task/update locales (#7084) Co-authored-by: github-actions[bot] --- apps/client/src/locales/messages.ca.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.de.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.es.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.fr.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.it.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.ko.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.nl.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.pl.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.pt.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.tr.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.uk.xlf | 154 ++++++++++++++---------- apps/client/src/locales/messages.xlf | 146 ++++++++++++---------- apps/client/src/locales/messages.zh.xlf | 154 ++++++++++++++---------- 13 files changed, 1177 insertions(+), 817 deletions(-) diff --git a/apps/client/src/locales/messages.ca.xlf b/apps/client/src/locales/messages.ca.xlf index be57f85c6..76caa9c1e 100644 --- a/apps/client/src/locales/messages.ca.xlf +++ b/apps/client/src/locales/messages.ca.xlf @@ -659,7 +659,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -707,7 +707,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -757,10 +757,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -946,6 +942,14 @@ 50 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Nombre d’Activitats @@ -974,6 +978,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count Nombre de Països @@ -1027,7 +1039,7 @@ El preu de mercat actual és apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -1159,7 +1171,7 @@ Configuració del Proveïdor de Dades apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -1167,7 +1179,7 @@ Prova apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -1179,7 +1191,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1195,7 +1207,7 @@ Asset profile has been saved apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -1351,7 +1363,7 @@ Recollida de Dades apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -1463,7 +1475,7 @@ Current year apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -1603,11 +1615,11 @@ Could not validate form apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -1983,7 +1995,7 @@ Current week apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2407,7 +2419,7 @@ YTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -2419,7 +2431,7 @@ 1 any apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -2431,7 +2443,7 @@ 5 anys apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -2451,7 +2463,7 @@ Màx apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -2679,7 +2691,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -2703,7 +2715,7 @@ Localització apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2886,6 +2898,14 @@ 193 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Ups! Sembla que esteu fent massa sol·licituds. Si us plau, aneu una mica més lent. @@ -3315,11 +3335,11 @@ Could not parse scraper configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -3547,6 +3567,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Holdings Explotacions @@ -4239,14 +4267,6 @@ 239 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Activitats d’importació @@ -4496,7 +4516,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -4695,14 +4715,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Close Holding Close Holding @@ -4879,6 +4891,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Transaccions il·limitades @@ -4983,6 +5003,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + Email and Chat Support Suport per correu electrònic i xat @@ -5004,11 +5032,11 @@ Could not save asset profile apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -5577,7 +5605,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -5597,7 +5625,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -5617,7 +5645,7 @@ any apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -5637,7 +5665,7 @@ anys apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6705,7 +6733,7 @@ Error apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6757,7 +6785,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6809,7 +6837,7 @@ Close apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6837,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7355,7 +7383,7 @@ Save apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7463,7 +7491,7 @@ Lazy apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7471,7 +7499,7 @@ Instant apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7479,7 +7507,7 @@ Default Market Price apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7487,7 +7515,7 @@ Mode apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7495,7 +7523,7 @@ Selector apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7503,7 +7531,7 @@ HTTP Request Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7511,7 +7539,7 @@ end of day apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7519,7 +7547,7 @@ real-time apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7720,7 +7748,7 @@ () is already in use. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7728,7 +7756,7 @@ An error occurred while updating to (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8079,7 +8107,7 @@ Current month apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index 3c8ed9e93..e1f501f28 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -314,7 +314,7 @@ Bezahlt apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -364,10 +364,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -418,7 +414,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -761,6 +757,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Sign in Einloggen @@ -1102,7 +1106,7 @@ YTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -1114,7 +1118,7 @@ 1J apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -1126,7 +1130,7 @@ 5J apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -1146,7 +1150,7 @@ Max apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -1334,7 +1338,7 @@ Gutschein apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -1342,7 +1346,7 @@ Lokalität apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1902,7 +1906,7 @@ Testphase apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -1998,7 +2002,7 @@ Aktuelle Woche apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2345,14 +2349,6 @@ 167 - - Subscriptions - Abonnements - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Aktivitäten importieren @@ -2689,6 +2685,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Fear Angst @@ -2762,11 +2766,11 @@ Das Formular konnte nicht validiert werden apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -3233,6 +3237,14 @@ 166 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Anzahl Aktivitäten @@ -3429,14 +3441,6 @@ 93 - - Expires - Läuft ab - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Import Dividends Dividenden importieren @@ -3681,6 +3685,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Unlimitierte Transaktionen @@ -3765,6 +3777,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + One-time payment, no auto-renewal. Einmalige Zahlung, keine automatische Erneuerung. @@ -3778,11 +3798,11 @@ Das Anlageprofil konnte nicht gespeichert werden apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -4014,7 +4034,7 @@ Aktuelles Jahr apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -4034,7 +4054,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -4050,7 +4070,7 @@ Das Anlageprofil wurde gespeichert apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -4426,7 +4446,7 @@ Scraper Konfiguration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -4958,11 +4978,11 @@ Die Scraper Konfiguration konnte nicht geparsed werden apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -5972,7 +5992,7 @@ Der aktuelle Marktpreis ist apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5980,7 +6000,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6136,7 +6156,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6156,7 +6176,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6204,7 +6224,7 @@ Jahr apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6224,7 +6244,7 @@ Jahre apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6244,7 +6264,7 @@ Finanzmarktdaten synchronisieren apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6304,6 +6324,14 @@ 246 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Ups! Es sieht so aus, als würdest du zu viele Anfragen senden. Bitte geh es ein bisschen langsamer an. @@ -6729,7 +6757,7 @@ Fehler apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6781,7 +6809,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6833,7 +6861,7 @@ Schliessen apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6861,7 +6889,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7379,7 +7407,7 @@ Speichern apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7487,7 +7515,7 @@ Verzögert apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7495,7 +7523,7 @@ Sofort apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7503,7 +7531,7 @@ Standardmarktpreis apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7511,7 +7539,7 @@ Modus apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7519,7 +7547,7 @@ Selektor apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7527,7 +7555,7 @@ HTTP Request-Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7535,7 +7563,7 @@ Tagesende apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7543,7 +7571,7 @@ in Echtzeit apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7744,7 +7772,7 @@ () wird bereits verwendet. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7752,7 +7780,7 @@ Bei der Änderung zu () ist ein Fehler aufgetreten. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8079,7 +8107,7 @@ Aktueller Monat apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.es.xlf b/apps/client/src/locales/messages.es.xlf index 97398f8b0..1b7dc66b7 100644 --- a/apps/client/src/locales/messages.es.xlf +++ b/apps/client/src/locales/messages.es.xlf @@ -315,7 +315,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -365,10 +365,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -419,7 +415,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -746,6 +742,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Sign in Iniciar sesión @@ -1087,7 +1091,7 @@ YTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -1099,7 +1103,7 @@ 1 año apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -1111,7 +1115,7 @@ 5 años apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -1131,7 +1135,7 @@ Máximo apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -1319,7 +1323,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -1327,7 +1331,7 @@ Configuración regional apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1887,7 +1891,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -1983,7 +1987,7 @@ Semana actual apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2330,14 +2334,6 @@ 167 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Importar operaciones @@ -2666,6 +2662,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count Número de países @@ -2759,11 +2763,11 @@ No se pudo validar el formulario apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -3218,6 +3222,14 @@ 166 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Número de operaciones @@ -3414,14 +3426,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Import Dividends Importar dividendos @@ -3666,6 +3670,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Operaciones ilimitadas @@ -3750,6 +3762,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + One-time payment, no auto-renewal. Pago único, sin renovación automática. @@ -3763,11 +3783,11 @@ No se pudo guardar el perfil del activo apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -3991,7 +4011,7 @@ Año actual apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -4011,7 +4031,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -4027,7 +4047,7 @@ Perfil del activo guardado apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -4403,7 +4423,7 @@ Configuración del scraper apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -4935,11 +4955,11 @@ No se pudo analizar la configuración del scraper apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -5949,7 +5969,7 @@ El precio actual de mercado es apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5957,7 +5977,7 @@ Prueba apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6113,7 +6133,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6133,7 +6153,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6181,7 +6201,7 @@ año apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6201,7 +6221,7 @@ años apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6221,7 +6241,7 @@ Recopilación de datos apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6281,6 +6301,14 @@ 246 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. ¡Vaya! Parece que estás haciendo demasiadas solicitudes. Por favor, reduce la velocidad un poco. @@ -6706,7 +6734,7 @@ Error apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6758,7 +6786,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6810,7 +6838,7 @@ Cerrar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6838,7 +6866,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7356,7 +7384,7 @@ Guardar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7464,7 +7492,7 @@ Bajo demanda apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7472,7 +7500,7 @@ Instantáneo apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7480,7 +7508,7 @@ Precio de mercado por defecto apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7488,7 +7516,7 @@ Modo apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7496,7 +7524,7 @@ Selector apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7504,7 +7532,7 @@ Encabezados de solicitud HTTP apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7512,7 +7540,7 @@ final del día apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7520,7 +7548,7 @@ en tiempo real apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7721,7 +7749,7 @@ () ya está en uso. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7729,7 +7757,7 @@ Ocurrió un error al actualizar a (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8080,7 +8108,7 @@ Mes actual apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.fr.xlf b/apps/client/src/locales/messages.fr.xlf index 25910eafe..d4d2d15e4 100644 --- a/apps/client/src/locales/messages.fr.xlf +++ b/apps/client/src/locales/messages.fr.xlf @@ -370,7 +370,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -412,10 +412,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -474,7 +470,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -581,6 +577,14 @@ 50 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Nombre d’Activités @@ -609,6 +613,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count Nombre de Pays @@ -910,11 +922,11 @@ Could not validate form apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -1342,7 +1354,7 @@ CDA apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -1354,7 +1366,7 @@ 1A apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -1366,7 +1378,7 @@ 5A apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -1386,7 +1398,7 @@ Max apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -1622,7 +1634,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -1638,7 +1650,7 @@ Paramètres régionaux apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2170,7 +2182,7 @@ Current week apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2410,7 +2422,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -2697,6 +2709,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Registration Enregistrement @@ -2841,14 +2861,6 @@ 167 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Importer Activités @@ -3413,14 +3425,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Import Dividends Importer Dividendes @@ -3665,6 +3669,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Transactions Illimitées @@ -3749,6 +3761,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + One-time payment, no auto-renewal. Paiement unique, sans auto-renouvellement. @@ -3762,11 +3782,11 @@ Could not save asset profile apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -3990,7 +4010,7 @@ Current year apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -4010,7 +4030,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -4026,7 +4046,7 @@ Asset profile has been saved apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -4402,7 +4422,7 @@ Configuration du Scraper apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -4934,11 +4954,11 @@ Could not parse scraper configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -5948,7 +5968,7 @@ Le prix actuel du marché est apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5956,7 +5976,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6112,7 +6132,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6132,7 +6152,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6180,7 +6200,7 @@ année apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6200,7 +6220,7 @@ années apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6220,7 +6240,7 @@ Collecter les données apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6280,6 +6300,14 @@ 246 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Oops! Il semble que vous fassiez trop de requêtes. Veuillez ralentir un peu. @@ -6705,7 +6733,7 @@ Erreur apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6757,7 +6785,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6809,7 +6837,7 @@ Fermer apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6837,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7355,7 +7383,7 @@ Sauvegarder apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7463,7 +7491,7 @@ Paresseux apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7471,7 +7499,7 @@ Instantané apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7479,7 +7507,7 @@ Prix du marché par défaut apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7487,7 +7515,7 @@ Mode apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7495,7 +7523,7 @@ Selecteur apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7503,7 +7531,7 @@ En-têtes de requête HTTP apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7511,7 +7539,7 @@ fin de journée apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7519,7 +7547,7 @@ temps réel apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7720,7 +7748,7 @@ () est déjà utilisé. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7728,7 +7756,7 @@ Une erreur s’est produite lors de la mise à jour vers (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8079,7 +8107,7 @@ Current month apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 56cd10a65..ffc753de9 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -315,7 +315,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -365,10 +365,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -419,7 +415,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -746,6 +742,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Sign in Accedi @@ -1087,7 +1091,7 @@ anno corrente apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -1099,7 +1103,7 @@ 1 anno apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -1111,7 +1115,7 @@ 5 anni apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -1131,7 +1135,7 @@ Massimo apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -1319,7 +1323,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -1327,7 +1331,7 @@ Locale apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1887,7 +1891,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -1983,7 +1987,7 @@ Current week apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2330,14 +2334,6 @@ 167 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Importa le attività @@ -2666,6 +2662,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count Numero di paesi @@ -2759,11 +2763,11 @@ Could not validate form apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -3218,6 +3222,14 @@ 166 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Conteggio attività @@ -3414,14 +3426,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Import Dividends Importa i dividendi @@ -3666,6 +3670,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Transazioni illimitate @@ -3750,6 +3762,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + One-time payment, no auto-renewal. Pagamento una tantum, senza rinnovo automatico. @@ -3763,11 +3783,11 @@ Could not save asset profile apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -3991,7 +4011,7 @@ Current year apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -4011,7 +4031,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -4027,7 +4047,7 @@ Asset profile has been saved apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -4403,7 +4423,7 @@ Configurazione dello scraper apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -4935,11 +4955,11 @@ Could not parse scraper configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -5949,7 +5969,7 @@ L’attuale prezzo di mercato è apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5957,7 +5977,7 @@ Prova apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6113,7 +6133,7 @@ Settimana corrente apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6133,7 +6153,7 @@ Mese corrente apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6181,7 +6201,7 @@ anno apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6201,7 +6221,7 @@ anni apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6221,7 +6241,7 @@ Raccolta Dati apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6281,6 +6301,14 @@ 246 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Ops! Sembra tu stia facendo troppe richieste. Rallenta un po’ per favore. @@ -6706,7 +6734,7 @@ Errore apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6758,7 +6786,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6810,7 +6838,7 @@ Chiudi apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6838,7 +6866,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7356,7 +7384,7 @@ Salva apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7464,7 +7492,7 @@ Pigro apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7472,7 +7500,7 @@ Istantaneo apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7480,7 +7508,7 @@ Prezzo di mercato predefinito apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7488,7 +7516,7 @@ Modalità apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7496,7 +7524,7 @@ Selettore apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7504,7 +7532,7 @@ Intestazioni della richiesta HTTP apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7512,7 +7540,7 @@ fine giornata apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7520,7 +7548,7 @@ in tempo reale apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7721,7 +7749,7 @@ () e gia in uso. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7729,7 +7757,7 @@ Si è verificato un errore durante l’aggiornamento di (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8080,7 +8108,7 @@ Current month apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.ko.xlf b/apps/client/src/locales/messages.ko.xlf index 2ac85bbf5..29e35fbb7 100644 --- a/apps/client/src/locales/messages.ko.xlf +++ b/apps/client/src/locales/messages.ko.xlf @@ -600,7 +600,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -620,7 +620,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -662,10 +662,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -847,6 +843,14 @@ 50 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count 거래 건수 @@ -875,6 +879,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count 국가 수 @@ -1064,7 +1076,7 @@ 스크래퍼 설정 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -1284,7 +1296,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1300,7 +1312,7 @@ 자산 정보가 저장되었습니다. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -1340,7 +1352,7 @@ 올해 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -1480,11 +1492,11 @@ 양식 유효성 검사에 실패했습니다. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -1736,7 +1748,7 @@ 이번주 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2220,7 +2232,7 @@ 연초 대비 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -2232,7 +2244,7 @@ 1년 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -2244,7 +2256,7 @@ 5년 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -2264,7 +2276,7 @@ 맥스 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -2428,7 +2440,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -2444,7 +2456,7 @@ 장소 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -3028,11 +3040,11 @@ 스크래퍼 설정을 파싱할 수 없습니다. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -3235,6 +3247,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Holdings 보유 종목 @@ -3919,14 +3939,6 @@ 239 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities 거래 내역 가져오기 @@ -4176,7 +4188,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -4343,14 +4355,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Top 상위 @@ -4487,6 +4491,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions 무제한 거래 @@ -4591,6 +4603,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + Email and Chat Support 이메일 및 채팅 지원 @@ -4628,11 +4648,11 @@ 자산 정보를 저장할 수 없습니다. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -5981,7 +6001,7 @@ 현재 시장가격은 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5989,7 +6009,7 @@ 시험 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6153,7 +6173,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6165,7 +6185,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6205,7 +6225,7 @@ 년도 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6225,7 +6245,7 @@ 연령 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6270,7 +6290,7 @@ 데이터 수집 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6305,6 +6325,14 @@ 240 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. 이런! 요청을 너무 많이 하시는 것 같습니다. 조금 천천히 해주세요. @@ -6730,7 +6758,7 @@ 오류 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6742,7 +6770,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6826,7 +6854,7 @@ 닫다 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6854,7 +6882,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7380,7 +7408,7 @@ 구하다 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7488,7 +7516,7 @@ 방법 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7496,7 +7524,7 @@ 기본 시장 가격 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7504,7 +7532,7 @@ 선택자 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7512,7 +7540,7 @@ 즉각적인 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7520,7 +7548,7 @@ 게으른 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7528,7 +7556,7 @@ HTTP 요청 헤더 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7536,7 +7564,7 @@ 실시간 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7544,7 +7572,7 @@ 하루의 끝 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7745,7 +7773,7 @@ ()은(는) 이미 사용 중입니다. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7753,7 +7781,7 @@ ()로 업데이트하는 동안 오류가 발생했습니다. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8080,7 +8108,7 @@ 이번 달 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.nl.xlf b/apps/client/src/locales/messages.nl.xlf index b7f163afe..fea2393fb 100644 --- a/apps/client/src/locales/messages.nl.xlf +++ b/apps/client/src/locales/messages.nl.xlf @@ -314,7 +314,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -364,10 +364,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -418,7 +414,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -745,6 +741,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Sign in Aanmelden @@ -1086,7 +1090,7 @@ YTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -1098,7 +1102,7 @@ 1J apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -1110,7 +1114,7 @@ 5J apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -1130,7 +1134,7 @@ Max apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -1318,7 +1322,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -1326,7 +1330,7 @@ Locatie apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -1886,7 +1890,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -1982,7 +1986,7 @@ Huidige week apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2329,14 +2333,6 @@ 167 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Activiteiten importeren @@ -2665,6 +2661,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count Aantal landen @@ -2758,11 +2762,11 @@ Het formulier kon niet worden gevalideerd. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -3217,6 +3221,14 @@ 166 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Aantal activiteiten @@ -3413,14 +3425,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Import Dividends Importeer dividenden @@ -3665,6 +3669,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Onbeperkte transacties @@ -3749,6 +3761,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + One-time payment, no auto-renewal. Eenmalige betaling, geen automatische verlenging. @@ -3762,11 +3782,11 @@ Kon het assetprofiel niet opslaan apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -3990,7 +4010,7 @@ Huidig jaar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -4010,7 +4030,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -4026,7 +4046,7 @@ Het activaprofiel is opgeslagen. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -4402,7 +4422,7 @@ Scraper instellingen apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -4934,11 +4954,11 @@ De scraperconfiguratie kon niet worden geparseerd apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -5948,7 +5968,7 @@ De huidige markt waarde is apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5956,7 +5976,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6112,7 +6132,7 @@ Week tot nu toe apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6132,7 +6152,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6180,7 +6200,7 @@ jaar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6200,7 +6220,7 @@ jaren apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6220,7 +6240,7 @@ Data Verzamelen apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6280,6 +6300,14 @@ 246 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Oeps! Het lijkt er op dat u te veel verzoeken indient. Doe het iets rustiger aan alstublieft. @@ -6705,7 +6733,7 @@ Fout apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6757,7 +6785,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6809,7 +6837,7 @@ Sluiten apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6837,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7355,7 +7383,7 @@ Opslaan apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7463,7 +7491,7 @@ Lui apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7471,7 +7499,7 @@ Direct apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7479,7 +7507,7 @@ Standaard Marktprijs apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7487,7 +7515,7 @@ Modus apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7495,7 +7523,7 @@ Kiezer apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7503,7 +7531,7 @@ HTTP Verzoek Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7511,7 +7539,7 @@ eind van de dag apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7519,7 +7547,7 @@ real-time apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7720,7 +7748,7 @@ () is al in gebruik. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7728,7 +7756,7 @@ Er is een fout opgetreden tijdens het updaten naar (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8079,7 +8107,7 @@ Huidige maand apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.pl.xlf b/apps/client/src/locales/messages.pl.xlf index b9621a202..2fcbca736 100644 --- a/apps/client/src/locales/messages.pl.xlf +++ b/apps/client/src/locales/messages.pl.xlf @@ -591,7 +591,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -611,7 +611,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -653,10 +653,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -838,6 +834,14 @@ 50 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Liczba Aktywności @@ -866,6 +870,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count Liczba Krajów @@ -1031,7 +1043,7 @@ Konfiguracja Scrapera apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -1251,7 +1263,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1267,7 +1279,7 @@ Profil zasobu został zapisany apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -1307,7 +1319,7 @@ Obecny rok apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -1447,11 +1459,11 @@ Could not validate form apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -1703,7 +1715,7 @@ Obecny tydzień apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2187,7 +2199,7 @@ Liczony od początku roku (year-to-date) apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -2199,7 +2211,7 @@ 1 rok apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -2211,7 +2223,7 @@ 5 lat apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -2231,7 +2243,7 @@ Maksimum apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -2395,7 +2407,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -2411,7 +2423,7 @@ Ustawienia Regionalne apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2995,11 +3007,11 @@ Nie udało się przetworzyć konfiguracji scrapera apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -3202,6 +3214,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Holdings Inwestycje @@ -3886,14 +3906,6 @@ 239 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Importuj Aktywności @@ -4143,7 +4155,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -4310,14 +4322,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Top Największe wzrosty @@ -4454,6 +4458,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Nieograniczona Liczba Transakcji @@ -4558,6 +4570,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + Email and Chat Support Wsparcie przez E-mail i Czat @@ -4595,11 +4615,11 @@ Could not save asset profile apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -5948,7 +5968,7 @@ Obecna cena rynkowa wynosi apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5956,7 +5976,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6112,7 +6132,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6132,7 +6152,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6180,7 +6200,7 @@ rok apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6200,7 +6220,7 @@ lata apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6220,7 +6240,7 @@ Gromadzenie Danych apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6280,6 +6300,14 @@ 246 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Ups! Wygląda na to, że wykonujesz zbyt wiele zapytań. Proszę, zwolnij trochę. @@ -6705,7 +6733,7 @@ Błąd apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6757,7 +6785,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6809,7 +6837,7 @@ Zamknij apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6837,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7355,7 +7383,7 @@ Zapisz apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7463,7 +7491,7 @@ Leniwy apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7471,7 +7499,7 @@ Natychmiastowy apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7479,7 +7507,7 @@ Domyślna cena rynkowa apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7487,7 +7515,7 @@ Tryb apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7495,7 +7523,7 @@ Selektor apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7503,7 +7531,7 @@ Nagłówki żądań HTTP apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7511,7 +7539,7 @@ koniec dnia apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7519,7 +7547,7 @@ w czasie rzeczywistym apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7720,7 +7748,7 @@ () jest już w użyciu. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7728,7 +7756,7 @@ Wystąpił błąd podczas aktualizacji do (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8079,7 +8107,7 @@ Bieżący miesiąc apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.pt.xlf b/apps/client/src/locales/messages.pt.xlf index eb4492290..e7baad543 100644 --- a/apps/client/src/locales/messages.pt.xlf +++ b/apps/client/src/locales/messages.pt.xlf @@ -370,7 +370,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -412,10 +412,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -474,7 +470,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -609,6 +605,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Gather Profile Data Recolher Dados de Perfíl @@ -778,11 +782,11 @@ Could not validate form apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -1338,7 +1342,7 @@ AATD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -1350,7 +1354,7 @@ 1A apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -1362,7 +1366,7 @@ 5A apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -1382,7 +1386,7 @@ Máx apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -1618,7 +1622,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -1646,7 +1650,7 @@ Localidade apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2150,7 +2154,7 @@ Current week apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2382,7 +2386,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -2633,6 +2637,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Registration Registo @@ -2741,14 +2753,6 @@ 167 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Importar Atividades @@ -3229,6 +3233,14 @@ 186 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Nº de Atividades @@ -3413,14 +3425,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Import Dividends Importar Dividendos @@ -3665,6 +3669,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Transações Ilimitadas @@ -3749,6 +3761,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + One-time payment, no auto-renewal. Pagamento único, sem renovação automática. @@ -3762,11 +3782,11 @@ Could not save asset profile apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -3990,7 +4010,7 @@ Current year apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -4010,7 +4030,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -4026,7 +4046,7 @@ Asset profile has been saved apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -4402,7 +4422,7 @@ Configuração do raspador apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -4934,11 +4954,11 @@ Could not parse scraper configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -5948,7 +5968,7 @@ O preço de mercado atual é apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5956,7 +5976,7 @@ Teste apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6112,7 +6132,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6132,7 +6152,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6180,7 +6200,7 @@ ano apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6200,7 +6220,7 @@ anos apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6220,7 +6240,7 @@ Coleta de dados apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6280,6 +6300,14 @@ 246 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Ops! Parece que você está fazendo muitas solicitações. Por favor, diminua um pouco a velocidade. @@ -6705,7 +6733,7 @@ Erro apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6757,7 +6785,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6809,7 +6837,7 @@ Fechar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6837,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7355,7 +7383,7 @@ Guardar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7463,7 +7491,7 @@ Lazy apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7471,7 +7499,7 @@ Instant apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7479,7 +7507,7 @@ Preço de mercado padrão apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7487,7 +7515,7 @@ Mode apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7495,7 +7523,7 @@ Selector apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7503,7 +7531,7 @@ HTTP Request Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7511,7 +7539,7 @@ end of day apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7519,7 +7547,7 @@ real-time apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7720,7 +7748,7 @@ () is already in use. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7728,7 +7756,7 @@ An error occurred while updating to (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8079,7 +8107,7 @@ Current month apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.tr.xlf b/apps/client/src/locales/messages.tr.xlf index 6b8c8d662..b2893c970 100644 --- a/apps/client/src/locales/messages.tr.xlf +++ b/apps/client/src/locales/messages.tr.xlf @@ -551,7 +551,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -593,10 +593,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -655,7 +651,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -790,6 +786,14 @@ 50 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count İşlem Sayısı @@ -818,6 +822,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count Ülke Sayısı @@ -951,7 +963,7 @@ Veri Toplayıcı Yapılandırması apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -1147,7 +1159,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1163,7 +1175,7 @@ Asset profile has been saved apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -1203,7 +1215,7 @@ Current year apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -1295,11 +1307,11 @@ Could not validate form apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -1551,7 +1563,7 @@ Current week apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2035,7 +2047,7 @@ YTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -2047,7 +2059,7 @@ 1Y apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -2059,7 +2071,7 @@ 5Y apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -2079,7 +2091,7 @@ Maks. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -2515,11 +2527,11 @@ Could not parse scraper configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -2734,6 +2746,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Holdings Varlıklar @@ -3318,14 +3338,6 @@ 239 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities İşlemleri İçe Aktar @@ -3583,7 +3595,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -3750,14 +3762,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Top Üst @@ -3894,6 +3898,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Sınırsız İşlem @@ -3998,6 +4010,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + Email and Chat Support E-posta ve Sohbet Desteği @@ -4035,11 +4055,11 @@ Could not save asset profile apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -4600,7 +4620,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -4616,7 +4636,7 @@ Yerel Ayarlar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -5948,7 +5968,7 @@ Şu anki piyasa fiyatı apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5956,7 +5976,7 @@ Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6112,7 +6132,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6132,7 +6152,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6180,7 +6200,7 @@ Yıl apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6200,7 +6220,7 @@ Yıllar apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6220,7 +6240,7 @@ Veri Toplama apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6280,6 +6300,14 @@ 246 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Oops! Görünüşe göre çok fazla istekte bulunuyorsunuz. Lütfen biraz yavaşlayın. @@ -6705,7 +6733,7 @@ Hata apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6757,7 +6785,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6809,7 +6837,7 @@ Kapat apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6837,7 +6865,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7355,7 +7383,7 @@ Kaydet apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7463,7 +7491,7 @@ Tembel apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7471,7 +7499,7 @@ Anında apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7479,7 +7507,7 @@ Varsayılan Piyasa Fiyatı apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7487,7 +7515,7 @@ Mod apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7495,7 +7523,7 @@ Seçici apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7503,7 +7531,7 @@ HTTP İstek Başlıkları apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7511,7 +7539,7 @@ gün sonu apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7519,7 +7547,7 @@ gerçek zamanlı apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7720,7 +7748,7 @@ () is already in use. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7728,7 +7756,7 @@ Güncelleştirilirken bir hata oluştu (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8079,7 +8107,7 @@ Current month apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.uk.xlf b/apps/client/src/locales/messages.uk.xlf index 7ea4854c1..85530b392 100644 --- a/apps/client/src/locales/messages.uk.xlf +++ b/apps/client/src/locales/messages.uk.xlf @@ -675,7 +675,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -723,7 +723,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -781,10 +781,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -934,6 +930,14 @@ 50 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count Кількість активностей @@ -962,6 +966,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count Кількість країн @@ -1007,7 +1019,7 @@ Помилка apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -1015,7 +1027,7 @@ Поточна ринкова ціна apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -1131,7 +1143,7 @@ Конфігурація скребка apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -1139,7 +1151,7 @@ Тест apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -1151,7 +1163,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1167,7 +1179,7 @@ Asset profile has been saved apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -1331,7 +1343,7 @@ Збір даних apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -1443,7 +1455,7 @@ Current year apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -1707,11 +1719,11 @@ Could not validate form apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -2111,7 +2123,7 @@ Current week apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2331,7 +2343,7 @@ Зберегти apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -2611,7 +2623,7 @@ З початку року apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -2623,7 +2635,7 @@ 1 рік apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -2635,7 +2647,7 @@ 5 років apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -2655,7 +2667,7 @@ Максимум apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -2959,7 +2971,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -2983,7 +2995,7 @@ Локалізація apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -3150,6 +3162,14 @@ 190 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. Упс! Здається, ви робите занадто багато запитів. Будь ласка, пригальмуй трохи. @@ -3587,11 +3607,11 @@ Could not parse scraper configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -3819,6 +3839,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Holdings Активи @@ -4539,14 +4567,6 @@ 239 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities Імпортувати активності @@ -4812,7 +4832,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -5011,14 +5031,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Close Holding Close Holding @@ -5235,6 +5247,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions Необмежені транзакції @@ -5339,6 +5359,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + Email and Chat Support Підтримка електронної пошти та чату @@ -5360,11 +5388,11 @@ Could not save asset profile apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -6343,7 +6371,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6363,7 +6391,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6383,7 +6411,7 @@ рік apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6403,7 +6431,7 @@ роки apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6759,7 +6787,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6819,7 +6847,7 @@ Закрити apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6847,7 +6875,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7463,7 +7491,7 @@ Лінивий apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7471,7 +7499,7 @@ Миттєвий apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7479,7 +7507,7 @@ Default Market Price apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7487,7 +7515,7 @@ Режим apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7495,7 +7523,7 @@ Селектор apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7503,7 +7531,7 @@ HTTP Request Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7511,7 +7539,7 @@ end of day apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7519,7 +7547,7 @@ реальний час apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7720,7 +7748,7 @@ () is already in use. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7728,7 +7756,7 @@ An error occurred while updating to (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8079,7 +8107,7 @@ Current month apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.xlf b/apps/client/src/locales/messages.xlf index 54f8a7bfc..c15799d5f 100644 --- a/apps/client/src/locales/messages.xlf +++ b/apps/client/src/locales/messages.xlf @@ -567,7 +567,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -585,7 +585,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -624,10 +624,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -792,6 +788,13 @@ 50 + + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count @@ -817,6 +820,13 @@ 174 + + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count @@ -988,7 +998,7 @@ Scraper Configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -1184,7 +1194,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1199,7 +1209,7 @@ Asset profile has been saved apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -1234,7 +1244,7 @@ Current year apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -1359,11 +1369,11 @@ Could not validate form apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -1592,7 +1602,7 @@ Current week apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2037,7 +2047,7 @@ YTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -2048,7 +2058,7 @@ 1Y apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -2059,7 +2069,7 @@ 5Y apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -2077,7 +2087,7 @@ Max apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -2223,7 +2233,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -2237,7 +2247,7 @@ Locale apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -2781,11 +2791,11 @@ Could not parse scraper configuration apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -2969,6 +2979,13 @@ 334 + + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Holdings @@ -3591,13 +3608,6 @@ 239 - - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities @@ -3820,7 +3830,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -3972,13 +3982,6 @@ 93 - - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Top @@ -4101,6 +4104,13 @@ 26 + + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions @@ -4195,6 +4205,13 @@ 193 + + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + Email and Chat Support @@ -4228,11 +4245,11 @@ Could not save asset profile apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -5443,14 +5460,14 @@ The current market price is apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 Test apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -5597,7 +5614,7 @@ MTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -5608,7 +5625,7 @@ WTD apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -5644,7 +5661,7 @@ year apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -5663,7 +5680,7 @@ years apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -5704,7 +5721,7 @@ Data Gathering apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -5736,6 +5753,13 @@ 240 + + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. @@ -6115,7 +6139,7 @@ Error apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6126,7 +6150,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6205,7 +6229,7 @@ Close apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6233,7 +6257,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -6703,7 +6727,7 @@ Save apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6803,56 +6827,56 @@ Mode apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 Default Market Price apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 Selector apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 Instant apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 Lazy apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 HTTP Request Headers apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 real-time apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 end of day apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7032,14 +7056,14 @@ () is already in use. apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 An error occurred while updating to (). apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -7331,7 +7355,7 @@ Current month apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 diff --git a/apps/client/src/locales/messages.zh.xlf b/apps/client/src/locales/messages.zh.xlf index 115b71897..f21944458 100644 --- a/apps/client/src/locales/messages.zh.xlf +++ b/apps/client/src/locales/messages.zh.xlf @@ -600,7 +600,7 @@ Paid apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 117 + 121 @@ -620,7 +620,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 453 + 468 @@ -662,10 +662,6 @@ apps/client/src/app/components/admin-jobs/admin-jobs.html 134 - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 140 - Finished @@ -847,6 +843,14 @@ 50 + + Data Gathering Frequency + Data Gathering Frequency + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html + 449 + + Activities Count 活动计数 @@ -875,6 +879,14 @@ 174 + + Subscription History + Subscription History + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 131 + + Countries Count 国家数 @@ -1040,7 +1052,7 @@ 刮削配置 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 476 + 491 @@ -1260,7 +1272,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 555 + 570 apps/client/src/app/components/admin-platform/admin-platform.component.html @@ -1276,7 +1288,7 @@ 资产概况已保存 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 624 + 645 @@ -1316,7 +1328,7 @@ 当前年份 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 @@ -1456,11 +1468,11 @@ 无法验证表单 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 600 + 621 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 603 + 624 @@ -1712,7 +1724,7 @@ 当前周 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 @@ -2196,7 +2208,7 @@ 年初至今 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 212 + 228 libs/ui/src/lib/assistant/assistant.component.ts @@ -2208,7 +2220,7 @@ 1年 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 libs/ui/src/lib/assistant/assistant.component.ts @@ -2220,7 +2232,7 @@ 5年 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -2240,7 +2252,7 @@ 最大限度 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 224 + 240 libs/ui/src/lib/assistant/assistant.component.ts @@ -2404,7 +2416,7 @@ Coupon apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 122 + 126 @@ -2420,7 +2432,7 @@ 语言环境 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 514 + 529 apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -3004,11 +3016,11 @@ 无法解析抓取器配置 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 551 + 569 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 554 + 572 @@ -3211,6 +3223,14 @@ 334 + + Creation + Creation + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 140 + + Holdings 持仓 @@ -3903,14 +3923,6 @@ 239 - - Subscriptions - Subscriptions - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 131 - - Import Activities 导入活动记录 @@ -4160,7 +4172,7 @@ Trial apps/client/src/app/components/user-detail-dialog/user-detail-dialog.component.ts - 121 + 125 @@ -4327,14 +4339,6 @@ 93 - - Expires - Expires - - apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 194 - - Top 顶部 @@ -4471,6 +4475,14 @@ 26 + + Hourly + Hourly + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 214 + + Unlimited Transactions 无限交易 @@ -4575,6 +4587,14 @@ 193 + + Expiration + Expiration + + apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html + 198 + + Email and Chat Support 电子邮件和聊天支持 @@ -4612,11 +4632,11 @@ 无法保存资产概况 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 634 + 655 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 637 + 658 @@ -5957,7 +5977,7 @@ 当前市场价格为 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 749 + 770 @@ -5965,7 +5985,7 @@ 测试 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 573 + 588 @@ -6129,7 +6149,7 @@ 本月至今 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 libs/ui/src/lib/assistant/assistant.component.ts @@ -6141,7 +6161,7 @@ 本周至今 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 204 + 220 libs/ui/src/lib/assistant/assistant.component.ts @@ -6181,7 +6201,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 216 + 232 apps/client/src/app/pages/resources/personal-finance-tools/product-page.html @@ -6201,7 +6221,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 220 + 236 libs/ui/src/lib/assistant/assistant.component.ts @@ -6246,7 +6266,7 @@ 数据收集 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 596 + 611 apps/client/src/app/components/admin-overview/admin-overview.html @@ -6281,6 +6301,14 @@ 240 + + Daily + Daily + + apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts + 210 + + Oops! It looks like you’re making too many requests. Please slow down a bit. 哎呀!看来您提出了太多要求。请慢一点。 @@ -6706,7 +6734,7 @@ 错误 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 740 + 761 @@ -6758,7 +6786,7 @@ apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 601 + 616 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6810,7 +6838,7 @@ 关闭 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 603 + 618 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -6838,7 +6866,7 @@ apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html - 229 + 233 apps/client/src/app/pages/accounts/create-or-update-account-dialog/create-or-update-account-dialog.html @@ -7356,7 +7384,7 @@ 保存 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 612 + 627 apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html @@ -7464,7 +7492,7 @@ 延迟 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7472,7 +7500,7 @@ 即时 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7480,7 +7508,7 @@ 默认市场价格 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 486 + 501 @@ -7488,7 +7516,7 @@ 模式 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 523 + 538 @@ -7496,7 +7524,7 @@ 选择器 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 539 + 554 @@ -7504,7 +7532,7 @@ HTTP 请求标头 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html - 499 + 514 @@ -7512,7 +7540,7 @@ 收盘 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 239 + 255 @@ -7520,7 +7548,7 @@ 实时 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 243 + 259 @@ -7721,7 +7749,7 @@ () 已在使用中。 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 681 + 702 @@ -7729,7 +7757,7 @@ 在更新到 () 时发生错误。 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 689 + 710 @@ -8080,7 +8108,7 @@ 当前月份 apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts - 208 + 224 From a645578e1fc2325c962807c3c8b8011077b57206 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 21 Jun 2026 10:02:01 +0200 Subject: [PATCH 09/60] Task/improve language localization for DE (20260621) (#7092) * Update translations * Update changelog --- CHANGELOG.md | 6 ++++++ apps/client/src/locales/messages.de.xlf | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c3558ccc..d5dd293bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Improved the language localization for German (`de`) + ## 3.13.0 - 2026-06-20 ### Added diff --git a/apps/client/src/locales/messages.de.xlf b/apps/client/src/locales/messages.de.xlf index e1f501f28..5ebf979ed 100644 --- a/apps/client/src/locales/messages.de.xlf +++ b/apps/client/src/locales/messages.de.xlf @@ -759,7 +759,7 @@ Creation - Creation + Erstellung apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html 140 @@ -2687,7 +2687,7 @@ Subscription History - Subscription History + Verlauf der Mitgliedschaft apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html 131 @@ -3239,7 +3239,7 @@ Data Gathering Frequency - Data Gathering Frequency + Häufigkeit der Datensynchronisierung apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html 449 @@ -3687,7 +3687,7 @@ Hourly - Hourly + Stündlich apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 214 @@ -3779,7 +3779,7 @@ Expiration - Expiration + Ablauf apps/client/src/app/components/user-detail-dialog/user-detail-dialog.html 198 @@ -6326,7 +6326,7 @@ Daily - Daily + Täglich apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts 210 From 1896289bc9b416a8b87b7543e62e13e3a14c9155 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 21 Jun 2026 10:22:29 +0200 Subject: [PATCH 10/60] Feature/expose ENABLE_FEATURE_CRON environment variable (#7090) * Expose ENABLE_FEATURE_CRON environment variable * Update changelog --- CHANGELOG.md | 4 ++ .../configuration/configuration.service.ts | 1 + apps/api/src/services/cron/cron.module.ts | 49 ++++++++++++++++++- .../interfaces/environment.interface.ts | 1 + 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5dd293bb..41bf233c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Exposed the `ENABLE_FEATURE_CRON` environment variable to control scheduled cron job execution + ### Changed - Improved the language localization for German (`de`) diff --git a/apps/api/src/services/configuration/configuration.service.ts b/apps/api/src/services/configuration/configuration.service.ts index 5f9d1055d..328620164 100644 --- a/apps/api/src/services/configuration/configuration.service.ts +++ b/apps/api/src/services/configuration/configuration.service.ts @@ -43,6 +43,7 @@ export class ConfigurationService { ENABLE_FEATURE_AUTH_GOOGLE: bool({ default: false }), ENABLE_FEATURE_AUTH_OIDC: bool({ default: false }), ENABLE_FEATURE_AUTH_TOKEN: bool({ default: true }), + ENABLE_FEATURE_CRON: bool({ default: true }), ENABLE_FEATURE_FEAR_AND_GREED_INDEX: bool({ default: false }), ENABLE_FEATURE_GATHER_NEW_EXCHANGE_RATES: bool({ default: true }), ENABLE_FEATURE_READ_ONLY_MODE: bool({ default: false }), diff --git a/apps/api/src/services/cron/cron.module.ts b/apps/api/src/services/cron/cron.module.ts index bcc9d3360..3d999e397 100644 --- a/apps/api/src/services/cron/cron.module.ts +++ b/apps/api/src/services/cron/cron.module.ts @@ -1,12 +1,19 @@ import { UserModule } from '@ghostfolio/api/app/user/user.module'; +import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; +import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; +import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; +import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; +import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { StatisticsGatheringQueueModule } from '@ghostfolio/api/services/queues/statistics-gathering/statistics-gathering.module'; +import { StatisticsGatheringService } from '@ghostfolio/api/services/queues/statistics-gathering/statistics-gathering.service'; import { TwitterBotModule } from '@ghostfolio/api/services/twitter-bot/twitter-bot.module'; +import { TwitterBotService } from '@ghostfolio/api/services/twitter-bot/twitter-bot.service'; -import { Module } from '@nestjs/common'; +import { Logger, Module } from '@nestjs/common'; import { CronService } from './cron.service'; @@ -20,6 +27,44 @@ import { CronService } from './cron.service'; TwitterBotModule, UserModule ], - providers: [CronService] + providers: [ + { + inject: [ + ConfigurationService, + DataGatheringService, + ExchangeRateDataService, + PropertyService, + StatisticsGatheringService, + TwitterBotService, + UserService + ], + provide: CronService, + useFactory: ( + configurationService: ConfigurationService, + dataGatheringService: DataGatheringService, + exchangeRateDataService: ExchangeRateDataService, + propertyService: PropertyService, + statisticsGatheringService: StatisticsGatheringService, + twitterBotService: TwitterBotService, + userService: UserService + ) => { + if (!configurationService.get('ENABLE_FEATURE_CRON')) { + Logger.log('Scheduled cron jobs are disabled', 'CronService'); + + return null; + } + + return new CronService( + configurationService, + dataGatheringService, + exchangeRateDataService, + propertyService, + statisticsGatheringService, + twitterBotService, + userService + ); + } + } + ] }) export class CronModule {} diff --git a/apps/api/src/services/interfaces/environment.interface.ts b/apps/api/src/services/interfaces/environment.interface.ts index 57c58898e..45654a2e3 100644 --- a/apps/api/src/services/interfaces/environment.interface.ts +++ b/apps/api/src/services/interfaces/environment.interface.ts @@ -19,6 +19,7 @@ export interface Environment extends CleanedEnvAccessors { ENABLE_FEATURE_AUTH_GOOGLE: boolean; ENABLE_FEATURE_AUTH_OIDC: boolean; ENABLE_FEATURE_AUTH_TOKEN: boolean; + ENABLE_FEATURE_CRON: boolean; ENABLE_FEATURE_FEAR_AND_GREED_INDEX: boolean; ENABLE_FEATURE_GATHER_NEW_EXCHANGE_RATES: boolean; ENABLE_FEATURE_READ_ONLY_MODE: boolean; From e5c694339ac434f1bc634e55f7dbbd36afd5c695 Mon Sep 17 00:00:00 2001 From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Sun, 21 Jun 2026 22:33:49 +0700 Subject: [PATCH 11/60] Task/improve type safety in transfer balance dialog component (#7080) Improve type safety --- .../transfer-balance/interfaces/interfaces.ts | 7 ++ .../transfer-balance-dialog.component.ts | 78 ++++++++++--------- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/apps/client/src/app/pages/accounts/transfer-balance/interfaces/interfaces.ts b/apps/client/src/app/pages/accounts/transfer-balance/interfaces/interfaces.ts index 3a0b921fd..51c42bc5d 100644 --- a/apps/client/src/app/pages/accounts/transfer-balance/interfaces/interfaces.ts +++ b/apps/client/src/app/pages/accounts/transfer-balance/interfaces/interfaces.ts @@ -1,5 +1,12 @@ +import { FormControl, FormGroup } from '@angular/forms'; import { Account } from '@prisma/client'; export interface TransferBalanceDialogParams { accounts: Account[]; } + +export type TransferBalanceForm = FormGroup<{ + balance: FormControl; + fromAccount: FormControl; + toAccount: FormControl; +}>; diff --git a/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts index 34a66b156..cbf0e460d 100644 --- a/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts +++ b/apps/client/src/app/pages/accounts/transfer-balance/transfer-balance-dialog.component.ts @@ -1,10 +1,9 @@ import { TransferBalanceDto } from '@ghostfolio/common/dtos'; import { GfEntityLogoComponent } from '@ghostfolio/ui/entity-logo'; -import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; import { - AbstractControl, - FormBuilder, + FormControl, FormGroup, ReactiveFormsModule, ValidationErrors, @@ -21,7 +20,10 @@ import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { Account } from '@prisma/client'; -import { TransferBalanceDialogParams } from './interfaces/interfaces'; +import { + TransferBalanceDialogParams, + TransferBalanceForm +} from './interfaces/interfaces'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -40,57 +42,63 @@ import { TransferBalanceDialogParams } from './interfaces/interfaces'; templateUrl: 'transfer-balance-dialog.html' }) export class GfTransferBalanceDialogComponent { - public accounts: Account[] = []; - public currency: string; - public transferBalanceForm: FormGroup; + protected readonly accounts: Account[] = + inject(MAT_DIALOG_DATA).accounts; + + protected currency: string; + + protected readonly transferBalanceForm: TransferBalanceForm = new FormGroup( + { + balance: new FormControl('', Validators.required), + fromAccount: new FormControl('', Validators.required), + toAccount: new FormControl('', Validators.required) + }, + { + validators: this.compareAccounts + } + ); - public constructor( - @Inject(MAT_DIALOG_DATA) public data: TransferBalanceDialogParams, - public dialogRef: MatDialogRef, - private formBuilder: FormBuilder - ) {} + private readonly dialogRef = + inject>(MatDialogRef); public ngOnInit() { - this.accounts = this.data.accounts; + this.transferBalanceForm.controls.fromAccount.valueChanges.subscribe( + (id) => { + const currency = this.accounts.find((account) => { + return account.id === id; + })?.currency; - this.transferBalanceForm = this.formBuilder.group( - { - balance: ['', Validators.required], - fromAccount: ['', Validators.required], - toAccount: ['', Validators.required] - }, - { - validators: this.compareAccounts + if (currency) { + this.currency = currency; + } } ); - - this.transferBalanceForm.get('fromAccount').valueChanges.subscribe((id) => { - this.currency = this.accounts.find((account) => { - return account.id === id; - }).currency; - }); } - public onCancel() { + protected onCancel() { this.dialogRef.close(); } - public onSubmit() { + protected onSubmit() { const account: TransferBalanceDto = { - accountIdFrom: this.transferBalanceForm.get('fromAccount').value, - accountIdTo: this.transferBalanceForm.get('toAccount').value, - balance: this.transferBalanceForm.get('balance').value + accountIdFrom: this.transferBalanceForm.controls.fromAccount.value ?? '', + accountIdTo: this.transferBalanceForm.controls.toAccount.value ?? '', + balance: Number(this.transferBalanceForm.controls.balance.value) }; this.dialogRef.close({ account }); } - private compareAccounts(control: AbstractControl): ValidationErrors { - const accountFrom = control.get('fromAccount'); - const accountTo = control.get('toAccount'); + private compareAccounts( + formGroup: TransferBalanceForm + ): ValidationErrors | null { + const accountFrom = formGroup.controls.fromAccount; + const accountTo = formGroup.controls.toAccount; if (accountFrom.value === accountTo.value) { return { invalid: true }; } + + return null; } } From af9889f0ff0a06b73a48a9ce3aa2d515108c235b Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Sun, 21 Jun 2026 17:38:48 +0200 Subject: [PATCH 12/60] Bugfix/missing asset profiles and historical data for symbols across data sources (#7089) * Fix missing asset profiles and historical data * Update changelog --- CHANGELOG.md | 4 ++ apps/api/src/app/admin/admin.service.ts | 12 +++- .../ghostfolio/ghostfolio.service.ts | 9 ++- .../endpoints/watchlist/watchlist.service.ts | 7 +- .../src/app/portfolio/portfolio.service.ts | 7 +- apps/api/src/app/symbol/symbol.service.ts | 12 +++- .../data-provider/data-provider.service.ts | 71 ++++++++++++------- .../exchange-rate-data.service.ts | 18 ++++- .../data-gathering.processor.ts | 21 ++++-- .../data-gathering/data-gathering.service.ts | 8 ++- 10 files changed, 125 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41bf233c0..e76ae0198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the language localization for German (`de`) +### Fixed + +- Fixed an issue in the data provider service where asset profiles and historical data could be missing for symbols that exist in multiple data sources by keying the responses by the asset profile identifier + ## 3.13.0 - 2026-06-20 ### Added diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 6dddbbca7..e50c0c77f 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -11,7 +11,10 @@ import { PROPERTY_IS_READ_ONLY_MODE, PROPERTY_IS_USER_SIGNUP_ENABLED } from '@ghostfolio/common/config'; -import { getCurrencyFromSymbol } from '@ghostfolio/common/helper'; +import { + getAssetProfileIdentifier, + getCurrencyFromSymbol +} from '@ghostfolio/common/helper'; import { AdminData, AdminUserResponse, @@ -68,14 +71,17 @@ export class AdminService { { dataSource, symbol } ]); - if (!assetProfiles[symbol]?.currency) { + const assetProfile = + assetProfiles[getAssetProfileIdentifier({ dataSource, symbol })]; + + if (!assetProfile?.currency) { throw new BadRequestException( `Asset profile not found for ${symbol} (${dataSource})` ); } return this.symbolProfileService.add( - assetProfiles[symbol] as Prisma.SymbolProfileCreateInput + assetProfile as Prisma.SymbolProfileCreateInput ); } catch (error) { if ( diff --git a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts index b84ca881f..caff86ae0 100644 --- a/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts +++ b/apps/api/src/app/endpoints/data-providers/ghostfolio/ghostfolio.service.ts @@ -16,6 +16,7 @@ import { DERIVED_CURRENCIES } from '@ghostfolio/common/config'; import { PROPERTY_DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER_MAX_REQUESTS } from '@ghostfolio/common/config'; +import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { DataProviderGhostfolioAssetProfileResponse, DataProviderHistoricalResponse, @@ -60,7 +61,13 @@ export class GhostfolioService { } ]) .then(async (assetProfiles) => { - const assetProfile = assetProfiles[symbol]; + const assetProfile = + assetProfiles[ + getAssetProfileIdentifier({ + symbol, + dataSource: dataProviderService.getName() + }) + ]; const dataSourceOrigin = DataSource.GHOSTFOLIO; if (assetProfile) { diff --git a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts index 78786c00b..88702da00 100644 --- a/apps/api/src/app/endpoints/watchlist/watchlist.service.ts +++ b/apps/api/src/app/endpoints/watchlist/watchlist.service.ts @@ -40,14 +40,17 @@ export class WatchlistService { { dataSource, symbol } ]); - if (!assetProfiles[symbol]?.currency) { + const assetProfile = + assetProfiles[getAssetProfileIdentifier({ dataSource, symbol })]; + + if (!assetProfile?.currency) { throw new BadRequestException( `Asset profile not found for ${symbol} (${dataSource})` ); } await this.symbolProfileService.add( - assetProfiles[symbol] as Prisma.SymbolProfileCreateInput + assetProfile as Prisma.SymbolProfileCreateInput ); } diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index d6a54b180..418e60401 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -889,10 +889,13 @@ export class PortfolioService { marketPrice ); - if (historicalData[symbol]) { + const historicalDataItems = + historicalData[getAssetProfileIdentifier({ dataSource, symbol })]; + + if (historicalDataItems) { let j = -1; for (const [date, { marketPrice }] of Object.entries( - historicalData[symbol] + historicalDataItems )) { while ( j + 1 < transactionPoints.length && diff --git a/apps/api/src/app/symbol/symbol.service.ts b/apps/api/src/app/symbol/symbol.service.ts index 732fcb23d..39d279de1 100644 --- a/apps/api/src/app/symbol/symbol.service.ts +++ b/apps/api/src/app/symbol/symbol.service.ts @@ -94,12 +94,17 @@ export class SymbolService { date = new Date(), symbol }: DataGatheringItem): Promise { + const assetProfileIdentifier = getAssetProfileIdentifier({ + dataSource, + symbol + }); + let historicalData: { - [symbol: string]: { + [assetProfileIdentifier: string]: { [date: string]: DataProviderHistoricalResponse; }; } = { - [symbol]: {} + [assetProfileIdentifier]: {} }; try { @@ -112,7 +117,8 @@ export class SymbolService { return { marketPrice: - historicalData?.[symbol]?.[format(date, DATE_FORMAT)]?.marketPrice + historicalData?.[assetProfileIdentifier]?.[format(date, DATE_FORMAT)] + ?.marketPrice }; } diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 6780f58f0..5d49848fa 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -86,12 +86,11 @@ export class DataProviderService implements OnModuleInit { return false; } - // TODO: Change symbol in response to assetProfileIdentifier public async getAssetProfiles(items: AssetProfileIdentifier[]): Promise<{ - [symbol: string]: Partial; + [assetProfileIdentifier: string]: Partial; }> { const response: { - [symbol: string]: Partial; + [assetProfileIdentifier: string]: Partial; } = {}; const itemsGroupedByDataSource = groupBy(items, ({ dataSource }) => { @@ -117,7 +116,12 @@ export class DataProviderService implements OnModuleInit { promises.push( promise.then((assetProfile) => { if (isCurrency(assetProfile?.currency)) { - response[symbol] = assetProfile; + response[ + getAssetProfileIdentifier({ + symbol, + dataSource: DataSource[dataSource] + }) + ] = { ...assetProfile, symbol }; } }) ); @@ -283,7 +287,7 @@ export class DataProviderService implements OnModuleInit { symbol } ]) - )?.[symbol]; + )?.[assetProfileIdentifier]; } catch {} if (!assetProfile?.name) { @@ -333,17 +337,20 @@ export class DataProviderService implements OnModuleInit { }); } - // TODO: Change symbol in response to assetProfileIdentifier public async getHistorical( aItems: AssetProfileIdentifier[], aGranularity: Granularity = 'month', from: Date, to: Date ): Promise<{ - [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; + [assetProfileIdentifier: string]: { + [date: string]: DataProviderHistoricalResponse; + }; }> { let response: { - [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; + [assetProfileIdentifier: string]: { + [date: string]: DataProviderHistoricalResponse; + }; } = {}; if (isEmpty(aItems) || !isValid(from) || !isValid(to)) { @@ -383,13 +390,20 @@ export class DataProviderService implements OnModuleInit { ORDER BY date;`; response = marketDataByGranularity.reduce((r, marketData) => { - const { date, marketPrice, symbol } = marketData; + const { dataSource, date, marketPrice, symbol } = marketData; - if (!r[symbol]) { - r[symbol] = {}; + const assetProfileIdentifier = getAssetProfileIdentifier({ + dataSource, + symbol + }); + + if (!r[assetProfileIdentifier]) { + r[assetProfileIdentifier] = {}; } - r[symbol][format(new Date(date), DATE_FORMAT)] = { marketPrice }; + r[assetProfileIdentifier][format(new Date(date), DATE_FORMAT)] = { + marketPrice + }; return r; }, {}); @@ -400,7 +414,6 @@ export class DataProviderService implements OnModuleInit { } } - // TODO: Change symbol in response to assetProfileIdentifier public async getHistoricalRaw({ assetProfileIdentifiers, from, @@ -410,7 +423,9 @@ export class DataProviderService implements OnModuleInit { from: Date; to: Date; }): Promise<{ - [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; + [assetProfileIdentifier: string]: { + [date: string]: DataProviderHistoricalResponse; + }; }> { for (const { currency, rootCurrency } of DERIVED_CURRENCIES) { if ( @@ -443,11 +458,14 @@ export class DataProviderService implements OnModuleInit { ); const result: { - [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; + [assetProfileIdentifier: string]: { + [date: string]: DataProviderHistoricalResponse; + }; } = {}; const promises: Promise<{ data: { [date: string]: DataProviderHistoricalResponse }; + dataSource: DataSource; symbol: string; }>[] = []; for (const { dataSource, symbol } of assetProfileIdentifiers) { @@ -465,6 +483,7 @@ export class DataProviderService implements OnModuleInit { promises.push( Promise.resolve({ data, + dataSource, symbol }) ); @@ -478,7 +497,7 @@ export class DataProviderService implements OnModuleInit { requestTimeout: ms('30 seconds') }) .then((data) => { - return { symbol, data: data?.[symbol] }; + return { dataSource, symbol, data: data?.[symbol] }; }) ); } @@ -488,22 +507,26 @@ export class DataProviderService implements OnModuleInit { try { const allData = await Promise.all(promises); - for (const { data, symbol } of allData) { + for (const { data, dataSource, symbol } of allData) { const currency = DERIVED_CURRENCIES.find(({ rootCurrency }) => { return `${DEFAULT_CURRENCY}${rootCurrency}` === symbol; }); if (currency) { // Add derived currency - result[`${DEFAULT_CURRENCY}${currency.currency}`] = - this.transformHistoricalData({ - allData, - currency: `${DEFAULT_CURRENCY}${currency.rootCurrency}`, - factor: currency.factor - }); + result[ + getAssetProfileIdentifier({ + dataSource, + symbol: `${DEFAULT_CURRENCY}${currency.currency}` + }) + ] = this.transformHistoricalData({ + allData, + currency: `${DEFAULT_CURRENCY}${currency.rootCurrency}`, + factor: currency.factor + }); } - result[symbol] = data; + result[getAssetProfileIdentifier({ dataSource, symbol })] = data; } } catch (error) { this.logger.error(error); diff --git a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts index d8a08c1c4..3b48ce292 100644 --- a/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts +++ b/apps/api/src/services/exchange-rate-data/exchange-rate-data.service.ts @@ -15,6 +15,7 @@ import { getYesterday, resetHours } from '@ghostfolio/common/helper'; +import { DataProviderHistoricalResponse } from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { @@ -163,7 +164,7 @@ export class ExchangeRateDataService { } public async loadCurrencies() { - const result = await this.dataProviderService.getHistorical( + const historicalData = await this.dataProviderService.getHistorical( this.currencyPairs, 'day', getYesterday(), @@ -177,8 +178,21 @@ export class ExchangeRateDataService { requestTimeout: ms('30 seconds') }); + const result: { + [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; + } = {}; + for (const { dataSource, symbol } of this.currencyPairs) { - const quote = quotes[getAssetProfileIdentifier({ dataSource, symbol })]; + const assetProfileIdentifier = getAssetProfileIdentifier({ + dataSource, + symbol + }); + + if (historicalData[assetProfileIdentifier]) { + result[symbol] = historicalData[assetProfileIdentifier]; + } + + const quote = quotes[assetProfileIdentifier]; if (isNumber(quote?.marketPrice)) { result[symbol] = { diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts b/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts index ee5cb838a..8b7e3489f 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.processor.ts @@ -10,7 +10,11 @@ import { GATHER_ASSET_PROFILE_PROCESS_JOB_NAME, GATHER_HISTORICAL_MARKET_DATA_PROCESS_JOB_NAME } from '@ghostfolio/common/config'; -import { DATE_FORMAT, getStartOfUtcDate } from '@ghostfolio/common/helper'; +import { + DATE_FORMAT, + getAssetProfileIdentifier, + getStartOfUtcDate +} from '@ghostfolio/common/helper'; import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; import { Process, Processor } from '@nestjs/bull'; @@ -114,6 +118,11 @@ export class DataGatheringProcessor { to: new Date() }); + const assetProfileIdentifier = getAssetProfileIdentifier({ + dataSource, + symbol + }); + const data: Prisma.MarketDataUpdateInput[] = []; let lastMarketPrice: number; @@ -131,12 +140,14 @@ export class DataGatheringProcessor { ) ) { if ( - historicalData[symbol]?.[format(currentDate, DATE_FORMAT)] - ?.marketPrice + historicalData[assetProfileIdentifier]?.[ + format(currentDate, DATE_FORMAT) + ]?.marketPrice ) { lastMarketPrice = - historicalData[symbol]?.[format(currentDate, DATE_FORMAT)] - ?.marketPrice; + historicalData[assetProfileIdentifier]?.[ + format(currentDate, DATE_FORMAT) + ]?.marketPrice; } if (lastMarketPrice) { diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts index aa6c952ef..ff65ebc83 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts @@ -131,7 +131,9 @@ export class DataGatheringService { }); const marketPrice = - historicalData[symbol][format(date, DATE_FORMAT)].marketPrice; + historicalData[getAssetProfileIdentifier({ dataSource, symbol })][ + format(date, DATE_FORMAT) + ].marketPrice; if (marketPrice) { return await this.prismaService.marketData.upsert({ @@ -176,7 +178,9 @@ export class DataGatheringService { assetProfileIdentifiers ); - for (const [symbol, assetProfile] of Object.entries(assetProfiles)) { + for (const assetProfile of Object.values(assetProfiles)) { + const { symbol } = assetProfile; + const symbolProfile = symbolProfiles.find( ({ symbol: symbolProfileSymbol }) => { return symbolProfileSymbol === symbol; From 26f0c358111806b12633f6ac1e140095922e0c37 Mon Sep 17 00:00:00 2001 From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Sun, 21 Jun 2026 22:43:15 +0700 Subject: [PATCH 13/60] Task/improve type safety in allocations page component (#7076) Improve type safety --- .../allocations/allocations-page.component.ts | 333 +++++++++--------- .../allocations/allocations-page.html | 2 +- .../allocations/interfaces/interfaces.ts | 6 + .../src/lib/interfaces/holding.interface.ts | 2 +- 4 files changed, 177 insertions(+), 166 deletions(-) create mode 100644 apps/client/src/app/pages/portfolio/allocations/interfaces/interfaces.ts diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts index 9aab27dc0..1ed8bfa66 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.component.ts @@ -12,7 +12,7 @@ import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; -import { Market, MarketAdvanced } from '@ghostfolio/common/types'; +import { MarketAdvanced } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; @@ -24,7 +24,9 @@ import { GfWorldMapChartComponent } from '@ghostfolio/ui/world-map-chart'; import { ChangeDetectorRef, Component, + computed, DestroyRef, + inject, OnInit } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; @@ -41,6 +43,9 @@ import { } from '@prisma/client'; import { isNumber } from 'lodash'; import { DeviceDetectorService } from 'ngx-device-detector'; +import { filter, switchMap, tap } from 'rxjs'; + +import { AllocationsPageParams } from './interfaces/interfaces'; @Component({ imports: [ @@ -57,21 +62,23 @@ import { DeviceDetectorService } from 'ngx-device-detector'; templateUrl: './allocations-page.html' }) export class GfAllocationsPageComponent implements OnInit { - public accounts: { + protected accounts: { [id: string]: Pick & { id: string; value: number; }; }; - public continents: { + protected continents: { [code: string]: { name: string; value: number }; }; - public countries: { + protected countries: { [code: string]: { name: string; value: number }; }; - public deviceType: string; - public hasImpersonationId: boolean; - public holdings: { + protected readonly deviceType = computed( + () => this.deviceDetectorService.deviceInfo().deviceType + ); + protected hasImpersonationId: boolean; + protected holdings: { [symbol: string]: Pick< PortfolioPosition['assetProfile'], | 'assetClass' @@ -82,28 +89,26 @@ export class GfAllocationsPageComponent implements OnInit { | 'name' > & { etfProvider: string; exchange?: string; value: number }; }; - public isLoading = false; - public markets: { - [key in Market]: { id: Market; valueInPercentage: number }; - }; - public marketsAdvanced: { + protected isLoading = false; + protected markets: PortfolioDetails['markets']; + protected marketsAdvanced: { [key in MarketAdvanced]: { id: MarketAdvanced; name: string; value: number; }; }; - public platforms: { + protected platforms: { [id: string]: Pick & { id: string; value: number; }; }; - public portfolioDetails: PortfolioDetails; - public sectors: { + protected portfolioDetails: PortfolioDetails; + protected sectors: { [name: string]: { name: string; value: number }; }; - public symbols: { + protected symbols: { [name: string]: { dataSource?: DataSource; name: string; @@ -111,38 +116,46 @@ export class GfAllocationsPageComponent implements OnInit { value: number; }; }; - public topHoldings: HoldingWithParents[]; - public topHoldingsMap: { + protected topHoldings: HoldingWithParents[]; + protected readonly UNKNOWN_KEY = UNKNOWN_KEY; + protected user: User; + + private topHoldingsMap: { [name: string]: { name: string; value: number }; }; - public totalValueInEtf = 0; - public UNKNOWN_KEY = UNKNOWN_KEY; - public user: User; - public worldMapChartFormat: string; - - public constructor( - private changeDetectorRef: ChangeDetectorRef, - private dataService: DataService, - private destroyRef: DestroyRef, - private deviceDetectorService: DeviceDetectorService, - private dialog: MatDialog, - private impersonationStorageService: ImpersonationStorageService, - private route: ActivatedRoute, - private router: Router, - private userService: UserService - ) { + private totalValueInEtf = 0; + + private readonly changeDetectorRef = inject(ChangeDetectorRef); + private readonly dataService = inject(DataService); + private readonly destroyRef = inject(DestroyRef); + private readonly deviceDetectorService = inject(DeviceDetectorService); + private readonly dialog = inject(MatDialog); + private readonly impersonationStorageService = inject( + ImpersonationStorageService + ); + private readonly route = inject(ActivatedRoute); + private readonly router = inject(Router); + private readonly userService = inject(UserService); + + public constructor() { this.route.queryParams .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((params) => { - if (params['accountId'] && params['accountDetailDialog']) { - this.openAccountDetailDialog(params['accountId']); + .subscribe( + ({ accountId, accountDetailDialog }: AllocationsPageParams) => { + if (accountId && accountDetailDialog) { + this.openAccountDetailDialog(accountId); + } } - }); + ); } - public ngOnInit() { - this.deviceType = this.deviceDetectorService.getDeviceInfo().deviceType; + protected get worldMapChartFormat(): string { + return this.showValuesInPercentage() + ? '{0}%' + : `{0} ${this.user?.settings?.baseCurrency}`; + } + public ngOnInit() { this.impersonationStorageService .onChangeHasImpersonation() .pipe(takeUntilDestroyed(this.destroyRef)) @@ -151,56 +164,58 @@ export class GfAllocationsPageComponent implements OnInit { }); this.userService.stateChanged - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((state) => { - if (state?.user) { + .pipe( + filter((state) => !!state?.user), + tap((state) => { this.user = state.user; - this.worldMapChartFormat = this.showValuesInPercentage() - ? `{0}%` - : `{0} ${this.user?.settings?.baseCurrency}`; - this.isLoading = true; this.initialize(); - this.fetchPortfolioDetails() - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((portfolioDetails) => { - this.initialize(); - - this.portfolioDetails = portfolioDetails; + this.changeDetectorRef.markForCheck(); + }), + switchMap(() => this.fetchPortfolioDetails()), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe((portfolioDetails) => { + this.initialize(); - this.initializeAllocationsData(); + this.portfolioDetails = portfolioDetails; - this.isLoading = false; + this.initializeAllocationsData(); - this.changeDetectorRef.markForCheck(); - }); + this.isLoading = false; - this.changeDetectorRef.markForCheck(); - } + this.changeDetectorRef.markForCheck(); }); this.initialize(); } - public onAccountChartClicked({ accountId }: { accountId: string }) { + protected onAccountChartClicked({ accountId }: { accountId: string }) { if (accountId && accountId !== UNKNOWN_KEY) { - this.router.navigate([], { + void this.router.navigate([], { queryParams: { accountId, accountDetailDialog: true } }); } } - public onSymbolChartClicked({ dataSource, symbol }: AssetProfileIdentifier) { + protected onSymbolChartClicked({ + dataSource, + symbol + }: AssetProfileIdentifier) { if (dataSource && symbol) { - this.router.navigate([], { + void this.router.navigate([], { queryParams: { dataSource, symbol, holdingDetailDialog: true } }); } } + protected showValuesInPercentage() { + return this.hasImpersonationId || this.user?.settings?.isRestrictedView; + } + private extractCurrency({ assetClass, assetSubClass, @@ -226,9 +241,9 @@ export class GfAllocationsPageComponent implements OnInit { name }: { assetSubClass: PortfolioPosition['assetProfile']['assetSubClass']; - name: string; + name?: string; }) { - if (assetSubClass === 'ETF') { + if (assetSubClass === 'ETF' && name) { const [firstWord] = name.split(' '); return firstWord; } @@ -298,7 +313,7 @@ export class GfAllocationsPageComponent implements OnInit { this.platforms = {}; this.portfolioDetails = { accounts: {}, - createdAt: undefined, + createdAt: new Date(), holdings: {}, platforms: {}, summary: undefined @@ -327,7 +342,7 @@ export class GfAllocationsPageComponent implements OnInit { let value = 0; if (this.showValuesInPercentage()) { - value = valueInPercentage; + value = valueInPercentage ?? 0; } else { value = valueInBaseCurrency; } @@ -342,30 +357,24 @@ export class GfAllocationsPageComponent implements OnInit { for (const [symbol, position] of Object.entries( this.portfolioDetails.holdings )) { - let value = 0; - - if (this.showValuesInPercentage()) { - value = position.allocationInPercentage; - } else { - value = position.valueInBaseCurrency; - } - this.holdings[symbol] = { - value, assetClass: position.assetProfile.assetClass || (UNKNOWN_KEY as AssetClass), - assetClassLabel: position.assetProfile.assetClassLabel || UNKNOWN_KEY, + assetClassLabel: position.assetProfile.assetClassLabel ?? UNKNOWN_KEY, assetSubClass: position.assetProfile.assetSubClass || (UNKNOWN_KEY as AssetSubClass), assetSubClassLabel: - position.assetProfile.assetSubClassLabel || UNKNOWN_KEY, + position.assetProfile.assetSubClassLabel ?? UNKNOWN_KEY, currency: this.extractCurrency(position.assetProfile), etfProvider: this.extractEtfProvider({ assetSubClass: position.assetProfile.assetSubClass, name: position.assetProfile.name }), exchange: position.exchange, - name: position.assetProfile.name + name: position.assetProfile.name, + value: this.showValuesInPercentage() + ? position.allocationInPercentage + : (position.valueInBaseCurrency ?? 0) }; // Prepare analysis data by continents, countries, holdings and sectors @@ -373,53 +382,50 @@ export class GfAllocationsPageComponent implements OnInit { if (position.assetProfile.countries.length > 0) { for (const country of position.assetProfile.countries) { const { code, continent, weight } = country; + const value = + (isNumber(position.valueInBaseCurrency) + ? position.valueInBaseCurrency + : position.valueInPercentage) ?? 0; + + const continentData = this.continents[continent]; - if (this.continents[continent]?.value) { - this.continents[continent].value += - weight * - (isNumber(position.valueInBaseCurrency) - ? position.valueInBaseCurrency - : position.valueInPercentage); + if (continentData) { + continentData.value += weight * value; } else { this.continents[continent] = { name: translate(continent), - value: - weight * - (isNumber(position.valueInBaseCurrency) - ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency - : this.portfolioDetails.holdings[symbol].valueInPercentage) + value: weight * value }; } - if (this.countries[code]?.value) { - this.countries[code].value += - weight * - (isNumber(position.valueInBaseCurrency) - ? position.valueInBaseCurrency - : position.valueInPercentage); + const countryData = this.countries[code]; + + if (countryData) { + countryData.value += weight * value; } else { this.countries[code] = { name: getCountryName({ code }), - value: - weight * - (isNumber(position.valueInBaseCurrency) - ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency - : this.portfolioDetails.holdings[symbol].valueInPercentage) + value: weight * value }; } } } else { - this.continents[UNKNOWN_KEY].value += isNumber( - position.valueInBaseCurrency - ) - ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency - : this.portfolioDetails.holdings[symbol].valueInPercentage; - - this.countries[UNKNOWN_KEY].value += isNumber( - position.valueInBaseCurrency - ) - ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency - : this.portfolioDetails.holdings[symbol].valueInPercentage; + const value = + (isNumber(position.valueInBaseCurrency) + ? position.valueInBaseCurrency + : position.valueInPercentage) ?? 0; + + const continentData = this.continents[UNKNOWN_KEY]; + + if (continentData) { + continentData.value += value; + } + + const countryData = this.countries[UNKNOWN_KEY]; + + if (countryData) { + countryData.value += value; + } } if (position.assetProfile.holdings.length > 0) { @@ -429,21 +435,18 @@ export class GfAllocationsPageComponent implements OnInit { valueInBaseCurrency } of position.assetProfile.holdings) { const normalizedAssetName = this.normalizeAssetName(name); + const value = isNumber(valueInBaseCurrency) + ? valueInBaseCurrency + : allocationInPercentage * (position.valueInPercentage ?? 0); + + const holdingData = this.topHoldingsMap[normalizedAssetName]; - if (this.topHoldingsMap[normalizedAssetName]?.value) { - this.topHoldingsMap[normalizedAssetName].value += isNumber( - valueInBaseCurrency - ) - ? valueInBaseCurrency - : allocationInPercentage * - this.portfolioDetails.holdings[symbol].valueInPercentage; + if (holdingData) { + holdingData.value += value; } else { this.topHoldingsMap[normalizedAssetName] = { name, - value: isNumber(valueInBaseCurrency) - ? valueInBaseCurrency - : allocationInPercentage * - this.portfolioDetails.holdings[symbol].valueInPercentage + value }; } } @@ -452,30 +455,33 @@ export class GfAllocationsPageComponent implements OnInit { if (position.assetProfile.sectors.length > 0) { for (const sector of position.assetProfile.sectors) { const { name, weight } = sector; + const value = + (isNumber(position.valueInBaseCurrency) + ? position.valueInBaseCurrency + : position.valueInPercentage) ?? 0; - if (this.sectors[name]?.value) { - this.sectors[name].value += - weight * - (isNumber(position.valueInBaseCurrency) - ? position.valueInBaseCurrency - : position.valueInPercentage); + const sectorData = this.sectors[name]; + + if (sectorData) { + sectorData.value += weight * value; } else { this.sectors[name] = { name: translate(name), - value: - weight * - (isNumber(position.valueInBaseCurrency) - ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency - : this.portfolioDetails.holdings[symbol].valueInPercentage) + value: weight * value }; } } } else { - this.sectors[UNKNOWN_KEY].value += isNumber( - position.valueInBaseCurrency - ) - ? this.portfolioDetails.holdings[symbol].valueInBaseCurrency - : this.portfolioDetails.holdings[symbol].valueInPercentage; + const value = + (isNumber(position.valueInBaseCurrency) + ? position.valueInBaseCurrency + : position.valueInPercentage) ?? 0; + + const sectorData = this.sectors[UNKNOWN_KEY]; + + if (sectorData) { + sectorData.value += value; + } } if (this.holdings[symbol].assetSubClass === 'ETF') { @@ -484,23 +490,26 @@ export class GfAllocationsPageComponent implements OnInit { this.symbols[prettifySymbol(symbol)] = { dataSource: position.assetProfile.dataSource, - name: position.assetProfile.name, + name: position.assetProfile.name ?? '', symbol: prettifySymbol(symbol), - value: isNumber(position.valueInBaseCurrency) - ? position.valueInBaseCurrency - : position.valueInPercentage + value: + (isNumber(position.valueInBaseCurrency) + ? position.valueInBaseCurrency + : position.valueInPercentage) ?? 0 }; } this.markets = this.portfolioDetails.markets; - Object.values(this.portfolioDetails.marketsAdvanced).forEach( - ({ id, valueInBaseCurrency, valueInPercentage }) => { - this.marketsAdvanced[id].value = isNumber(valueInBaseCurrency) - ? valueInBaseCurrency - : valueInPercentage; - } - ); + if (this.portfolioDetails.marketsAdvanced) { + Object.values(this.portfolioDetails.marketsAdvanced).forEach( + ({ id, valueInBaseCurrency, valueInPercentage }) => { + this.marketsAdvanced[id].value = isNumber(valueInBaseCurrency) + ? valueInBaseCurrency + : valueInPercentage; + } + ); + } for (const [ id, @@ -509,7 +518,7 @@ export class GfAllocationsPageComponent implements OnInit { let value = 0; if (this.showValuesInPercentage()) { - value = valueInPercentage; + value = valueInPercentage ?? 0; } else { value = valueInBaseCurrency; } @@ -522,12 +531,11 @@ export class GfAllocationsPageComponent implements OnInit { } this.topHoldings = Object.values(this.topHoldingsMap) - .map(({ name, value }) => { + .map(({ name, value }): HoldingWithParents => { if (this.showValuesInPercentage()) { return { name, - allocationInPercentage: value, - valueInBaseCurrency: null + allocationInPercentage: value }; } @@ -547,11 +555,12 @@ export class GfAllocationsPageComponent implements OnInit { } ); - return currentParentHolding + return currentParentHolding && + isNumber(currentParentHolding.valueInBaseCurrency) ? { allocationInPercentage: currentParentHolding.valueInBaseCurrency / value, - name: holding.assetProfile.name, + name: holding.assetProfile.name ?? '', position: holding, symbol: prettifySymbol(symbol), valueInBaseCurrency: @@ -596,26 +605,22 @@ export class GfAllocationsPageComponent implements OnInit { autoFocus: false, data: { accountId: aAccountId, - deviceType: this.deviceType, + deviceType: this.deviceType(), hasImpersonationId: this.hasImpersonationId, hasPermissionToCreateActivity: !this.hasImpersonationId && hasPermission(this.user?.permissions, permissions.createActivity) && !this.user?.settings?.isRestrictedView }, - height: this.deviceType === 'mobile' ? '98vh' : '80vh', - width: this.deviceType === 'mobile' ? '100vw' : '50rem' + height: this.deviceType() === 'mobile' ? '98vh' : '80vh', + width: this.deviceType() === 'mobile' ? '100vw' : '50rem' }); dialogRef .afterClosed() .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(() => { - this.router.navigate(['.'], { relativeTo: this.route }); + void this.router.navigate(['.'], { relativeTo: this.route }); }); } - - public showValuesInPercentage() { - return this.hasImpersonationId || this.user?.settings?.isRestrictedView; - } } diff --git a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html index a1000189b..d5c765958 100644 --- a/apps/client/src/app/pages/portfolio/allocations/allocations-page.html +++ b/apps/client/src/app/pages/portfolio/allocations/allocations-page.html @@ -115,7 +115,7 @@ [isInPercentage]="showValuesInPercentage()" [keys]="['symbol']" [locale]="user?.settings?.locale" - [showLabels]="deviceType !== 'mobile'" + [showLabels]="deviceType() !== 'mobile'" (proportionChartClicked)="onSymbolChartClicked($event)" /> diff --git a/apps/client/src/app/pages/portfolio/allocations/interfaces/interfaces.ts b/apps/client/src/app/pages/portfolio/allocations/interfaces/interfaces.ts new file mode 100644 index 000000000..dc90f5f95 --- /dev/null +++ b/apps/client/src/app/pages/portfolio/allocations/interfaces/interfaces.ts @@ -0,0 +1,6 @@ +import { Params } from '@angular/router'; + +export interface AllocationsPageParams extends Params { + accountDetailDialog?: string; + accountId?: string; +} diff --git a/libs/common/src/lib/interfaces/holding.interface.ts b/libs/common/src/lib/interfaces/holding.interface.ts index e963bc5a7..c63729a34 100644 --- a/libs/common/src/lib/interfaces/holding.interface.ts +++ b/libs/common/src/lib/interfaces/holding.interface.ts @@ -1,5 +1,5 @@ export interface Holding { allocationInPercentage: number; name: string; - valueInBaseCurrency: number; + valueInBaseCurrency?: number; } From 63979e9d7129eb09ca6e5a505026dc966ff21dea Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 22 Jun 2026 19:17:42 +0200 Subject: [PATCH 14/60] Task/upgrade undici to version 8.5.0 (#7096) * Upgrade undici to version 8.5.0 * Update changelog --- CHANGELOG.md | 1 + package-lock.json | 39 ++++++++++++++++++++++++++++++++++----- package.json | 2 +- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e76ae0198..3dd4209e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved the language localization for German (`de`) +- Upgraded `undici` from version `7.24.4` to `8.5.0` ### Fixed diff --git a/package-lock.json b/package-lock.json index 30cb9b2aa..0d1051190 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,7 +94,7 @@ "svgmap": "2.21.0", "tablemark": "4.1.0", "twitter-api-v2": "1.29.0", - "undici": "7.24.4", + "undici": "8.5.0", "yahoo-finance2": "3.15.3", "zone.js": "0.16.1" }, @@ -834,6 +834,16 @@ "tslib": "^2.1.0" } }, + "node_modules/@angular-devkit/build-angular/node_modules/undici": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", + "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/@angular-devkit/build-webpack": { "version": "0.2102.6", "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2102.6.tgz", @@ -1135,6 +1145,16 @@ } } }, + "node_modules/@angular/build/node_modules/undici": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", + "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/@angular/cdk": { "version": "21.2.5", "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.2.5.tgz", @@ -18023,6 +18043,15 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/cheerio/node_modules/undici": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/chevrotain": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-12.0.0.tgz", @@ -38156,12 +38185,12 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", - "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", + "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", "license": "MIT", "engines": { - "node": ">=20.18.1" + "node": ">=22.19.0" } }, "node_modules/undici-types": { diff --git a/package.json b/package.json index 1003366da..3b92dc2fa 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "svgmap": "2.21.0", "tablemark": "4.1.0", "twitter-api-v2": "1.29.0", - "undici": "7.24.4", + "undici": "8.5.0", "yahoo-finance2": "3.15.3", "zone.js": "0.16.1" }, From fcad7917be07217c7068f948023ca686fe4f9516 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 22 Jun 2026 19:19:24 +0200 Subject: [PATCH 15/60] Feature/expose PROCESSOR_GATHER_STATISTICS_CONCURRENCY environment variable (#7100) * Expose PROCESSOR_GATHER_STATISTICS_CONCURRENCY environment variable * Update changelog --- CHANGELOG.md | 1 + .../configuration/configuration.service.ts | 4 ++ .../interfaces/environment.interface.ts | 1 + .../statistics-gathering.processor.ts | 37 +++++++++++++++++-- libs/common/src/lib/config.ts | 1 + 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dd4209e1..671c9f935 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Exposed the `ENABLE_FEATURE_CRON` environment variable to control scheduled cron job execution +- Exposed the `PROCESSOR_GATHER_STATISTICS_CONCURRENCY` environment variable to control the concurrency of the statistics gathering queue processor ### Changed diff --git a/apps/api/src/services/configuration/configuration.service.ts b/apps/api/src/services/configuration/configuration.service.ts index 328620164..703a90c3a 100644 --- a/apps/api/src/services/configuration/configuration.service.ts +++ b/apps/api/src/services/configuration/configuration.service.ts @@ -6,6 +6,7 @@ import { DEFAULT_PORT, DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY, DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY, + DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY, DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY, DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT } from '@ghostfolio/common/config'; @@ -89,6 +90,9 @@ export class ConfigurationService { PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY: num({ default: DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY }), + PROCESSOR_GATHER_STATISTICS_CONCURRENCY: num({ + default: DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY + }), PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY: num({ default: DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY }), diff --git a/apps/api/src/services/interfaces/environment.interface.ts b/apps/api/src/services/interfaces/environment.interface.ts index 45654a2e3..11bdeabf6 100644 --- a/apps/api/src/services/interfaces/environment.interface.ts +++ b/apps/api/src/services/interfaces/environment.interface.ts @@ -45,6 +45,7 @@ export interface Environment extends CleanedEnvAccessors { PORT: number; PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY: number; PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY: number; + PROCESSOR_GATHER_STATISTICS_CONCURRENCY: number; PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY: number; PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT: number; REDIS_DB: number; diff --git a/apps/api/src/services/queues/statistics-gathering/statistics-gathering.processor.ts b/apps/api/src/services/queues/statistics-gathering/statistics-gathering.processor.ts index 7eefc101f..82f362d25 100644 --- a/apps/api/src/services/queues/statistics-gathering/statistics-gathering.processor.ts +++ b/apps/api/src/services/queues/statistics-gathering/statistics-gathering.processor.ts @@ -2,6 +2,7 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/con import { FetchService } from '@ghostfolio/api/services/fetch/fetch.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { + DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY, GATHER_STATISTICS_DOCKER_HUB_PULLS_PROCESS_JOB_NAME, GATHER_STATISTICS_GITHUB_CONTRIBUTORS_PROCESS_JOB_NAME, GATHER_STATISTICS_GITHUB_STARGAZERS_PROCESS_JOB_NAME, @@ -35,7 +36,14 @@ export class StatisticsGatheringProcessor { private readonly propertyService: PropertyService ) {} - @Process(GATHER_STATISTICS_DOCKER_HUB_PULLS_PROCESS_JOB_NAME) + @Process({ + concurrency: parseInt( + process.env.PROCESSOR_GATHER_STATISTICS_CONCURRENCY ?? + DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY.toString(), + 10 + ), + name: GATHER_STATISTICS_DOCKER_HUB_PULLS_PROCESS_JOB_NAME + }) public async gatherDockerHubPullsStatistics() { this.logger.log('Docker Hub pulls statistics gathering has been started'); @@ -49,7 +57,14 @@ export class StatisticsGatheringProcessor { this.logger.log('Docker Hub pulls statistics gathering has been completed'); } - @Process(GATHER_STATISTICS_GITHUB_CONTRIBUTORS_PROCESS_JOB_NAME) + @Process({ + concurrency: parseInt( + process.env.PROCESSOR_GATHER_STATISTICS_CONCURRENCY ?? + DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY.toString(), + 10 + ), + name: GATHER_STATISTICS_GITHUB_CONTRIBUTORS_PROCESS_JOB_NAME + }) public async gatherGitHubContributorsStatistics() { this.logger.log( 'GitHub contributors statistics gathering has been started' @@ -67,7 +82,14 @@ export class StatisticsGatheringProcessor { ); } - @Process(GATHER_STATISTICS_GITHUB_STARGAZERS_PROCESS_JOB_NAME) + @Process({ + concurrency: parseInt( + process.env.PROCESSOR_GATHER_STATISTICS_CONCURRENCY ?? + DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY.toString(), + 10 + ), + name: GATHER_STATISTICS_GITHUB_STARGAZERS_PROCESS_JOB_NAME + }) public async gatherGitHubStargazersStatistics() { this.logger.log('GitHub stargazers statistics gathering has been started'); @@ -83,7 +105,14 @@ export class StatisticsGatheringProcessor { ); } - @Process(GATHER_STATISTICS_UPTIME_PROCESS_JOB_NAME) + @Process({ + concurrency: parseInt( + process.env.PROCESSOR_GATHER_STATISTICS_CONCURRENCY ?? + DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY.toString(), + 10 + ), + name: GATHER_STATISTICS_UPTIME_PROCESS_JOB_NAME + }) public async gatherUptimeStatistics() { const monitorId = await this.propertyService.getByKey( PROPERTY_BETTER_UPTIME_MONITOR_ID diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index 4924aaeea..e21271ca1 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -88,6 +88,7 @@ export const DEFAULT_PAGE_SIZE = 50; export const DEFAULT_PORT = 3333; export const DEFAULT_PROCESSOR_GATHER_ASSET_PROFILE_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_GATHER_HISTORICAL_MARKET_DATA_CONCURRENCY = 1; +export const DEFAULT_PROCESSOR_GATHER_STATISTICS_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY = 1; export const DEFAULT_PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_TIMEOUT = 30000; From 9580cc94e5dfa3039638fd5cf89d1b08999826c1 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 22 Jun 2026 19:20:37 +0200 Subject: [PATCH 16/60] Bugfix/resolve exception in benchmark service when current market price unavailable (#7101) * Handle exception * Update changelog --- CHANGELOG.md | 1 + .../src/app/endpoints/benchmarks/benchmarks.service.ts | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 671c9f935..a20fd9645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed an issue in the data provider service where asset profiles and historical data could be missing for symbols that exist in multiple data sources by keying the responses by the asset profile identifier +- Resolved an exception in the benchmarks service when the current market price is unavailable ## 3.13.0 - 2026-06-20 diff --git a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts index 0b95880d4..1fe42ab0d 100644 --- a/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts +++ b/apps/api/src/app/endpoints/benchmarks/benchmarks.service.ts @@ -81,6 +81,14 @@ export class BenchmarksService { }) ]); + if (!currentSymbolItem) { + this.logger.error( + `No current market price is available for ${symbol} (${dataSource})` + ); + + return { marketData }; + } + const exchangeRates = await this.exchangeRateDataService.getExchangeRatesByCurrency({ startDate, From cd6382c342093e1b10afb4aae3fa9022b454be7d Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 22 Jun 2026 21:09:08 +0200 Subject: [PATCH 17/60] Task/extend report data glitch mail subject with symbol (#7102) Extend subject with symbol --- .../holding-detail-dialog.component.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index b8dede2cd..1eb0d4ab6 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -412,7 +412,6 @@ export class GfHoldingDetailDialogComponent implements OnInit { } } - this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=Ghostfolio Data Glitch Report&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${SymbolProfile?.symbol}%0DData Source: ${SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`; this.sectors = {}; this.SymbolProfile = SymbolProfile; @@ -427,16 +426,22 @@ export class GfHoldingDetailDialogComponent implements OnInit { this.value = value; - if (SymbolProfile?.assetClass) { - this.assetClass = translate(SymbolProfile?.assetClass); + const reportDataGlitchSubject = `Ghostfolio Data Glitch Report${ + this.SymbolProfile?.symbol ? ` (${this.SymbolProfile.symbol})` : '' + }`; + + this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=${reportDataGlitchSubject}&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${this.SymbolProfile?.symbol}%0DData Source: ${this.SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`; + + if (this.SymbolProfile?.assetClass) { + this.assetClass = translate(this.SymbolProfile?.assetClass); } - if (SymbolProfile?.assetSubClass) { - this.assetSubClass = translate(SymbolProfile?.assetSubClass); + if (this.SymbolProfile?.assetSubClass) { + this.assetSubClass = translate(this.SymbolProfile?.assetSubClass); } - if (SymbolProfile?.countries?.length > 0) { - for (const country of SymbolProfile.countries) { + if (this.SymbolProfile?.countries?.length > 0) { + for (const country of this.SymbolProfile.countries) { this.countries[country.code] = { name: getCountryName({ code: country.code }), value: country.weight @@ -444,8 +449,8 @@ export class GfHoldingDetailDialogComponent implements OnInit { } } - if (SymbolProfile?.sectors?.length > 0) { - for (const sector of SymbolProfile.sectors) { + if (this.SymbolProfile?.sectors?.length > 0) { + for (const sector of this.SymbolProfile.sectors) { this.sectors[sector.name] = { name: translate(sector.name), value: sector.weight From bf82353048d842a95bbec6c3fd538d93f882f089 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 22 Jun 2026 21:10:14 +0200 Subject: [PATCH 18/60] Task/upgrade @openrouter/ai-sdk-provider to version 2.9.1 (#7097) * Upgrade @openrouter/ai-sdk-provider to version 2.9.1 * Update changelog --- CHANGELOG.md | 1 + package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a20fd9645..7bd562978 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved the language localization for German (`de`) +- Upgraded `@openrouter/ai-sdk-provider` from version `2.9.0` to `2.9.1` - Upgraded `undici` from version `7.24.4` to `8.5.0` ### Fixed diff --git a/package-lock.json b/package-lock.json index 0d1051190..66f68198b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "@nestjs/platform-express": "11.1.21", "@nestjs/schedule": "6.1.3", "@nestjs/serve-static": "5.0.5", - "@openrouter/ai-sdk-provider": "2.9.0", + "@openrouter/ai-sdk-provider": "2.9.1", "@prisma/adapter-pg": "7.8.0", "@prisma/client": "7.8.0", "@simplewebauthn/browser": "13.2.2", @@ -10783,9 +10783,9 @@ } }, "node_modules/@openrouter/ai-sdk-provider": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-2.9.0.tgz", - "integrity": "sha512-Seva+NCa0WUQnJIUE5GzHsUv1WTIeyqwz0ELl2VtS6NP+eF+77yCXGFVOMbvoCM7QMjlnhv7931e89R+8pJdcQ==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-2.9.1.tgz", + "integrity": "sha512-okgq07Vdkro4CB5INbfhwa0e6VR1HS7sidNcfHN/MeXLJvX1JmQCff/vem6tcxwT9r1avyFrXSlfv9B28D/Pag==", "license": "Apache-2.0", "engines": { "node": ">=18" diff --git a/package.json b/package.json index 3b92dc2fa..143945699 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "@nestjs/platform-express": "11.1.21", "@nestjs/schedule": "6.1.3", "@nestjs/serve-static": "5.0.5", - "@openrouter/ai-sdk-provider": "2.9.0", + "@openrouter/ai-sdk-provider": "2.9.1", "@prisma/adapter-pg": "7.8.0", "@prisma/client": "7.8.0", "@simplewebauthn/browser": "13.2.2", From 6e97a3c97871a6660626bc2b9d82fc04619c4cf4 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 22 Jun 2026 21:19:37 +0200 Subject: [PATCH 19/60] Task/remove deprecated fear and greed index symbol (#7103) Remove deprecated fear and greed index symbol --- .../rapid-api/rapid-api.service.ts | 19 +++---------------- .../twitter-bot/twitter-bot.service.ts | 4 ++-- .../home-market/home-market.component.ts | 4 ++-- libs/common/src/lib/config.ts | 4 +++- libs/common/src/lib/helper.ts | 2 -- 5 files changed, 10 insertions(+), 23 deletions(-) diff --git a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts index 9941ae9eb..d8ba1ec67 100644 --- a/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts +++ b/apps/api/src/services/data-provider/rapid-api/rapid-api.service.ts @@ -8,10 +8,7 @@ import { GetSearchParams } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { FetchService } from '@ghostfolio/api/services/fetch/fetch.service'; -import { - ghostfolioFearAndGreedIndexSymbol, - ghostfolioFearAndGreedIndexSymbolStocks -} from '@ghostfolio/common/config'; +import { ghostfolioFearAndGreedIndexSymbolStocks } from '@ghostfolio/common/config'; import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper'; import { DataProviderHistoricalResponse, @@ -64,12 +61,7 @@ export class RapidApiService implements DataProviderInterface { [symbol: string]: { [date: string]: DataProviderHistoricalResponse }; }> { try { - if ( - [ - ghostfolioFearAndGreedIndexSymbol, - ghostfolioFearAndGreedIndexSymbolStocks - ].includes(symbol) - ) { + if (symbol === ghostfolioFearAndGreedIndexSymbolStocks) { const fgi = await this.getFearAndGreedIndex(); return { @@ -106,12 +98,7 @@ export class RapidApiService implements DataProviderInterface { try { const symbol = symbols[0]; - if ( - [ - ghostfolioFearAndGreedIndexSymbol, - ghostfolioFearAndGreedIndexSymbolStocks - ].includes(symbol) - ) { + if (symbol === ghostfolioFearAndGreedIndexSymbolStocks) { const fgi = await this.getFearAndGreedIndex(); return { diff --git a/apps/api/src/services/twitter-bot/twitter-bot.service.ts b/apps/api/src/services/twitter-bot/twitter-bot.service.ts index ffd0c5452..4e6fd5bb2 100644 --- a/apps/api/src/services/twitter-bot/twitter-bot.service.ts +++ b/apps/api/src/services/twitter-bot/twitter-bot.service.ts @@ -3,7 +3,7 @@ import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.s import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ghostfolioFearAndGreedIndexDataSourceStocks, - ghostfolioFearAndGreedIndexSymbol + ghostfolioFearAndGreedIndexSymbolStocks } from '@ghostfolio/common/config'; import { resolveFearAndGreedIndex, @@ -49,7 +49,7 @@ export class TwitterBotService implements OnModuleInit { const symbolItem = await this.symbolService.get({ dataGatheringItem: { dataSource: ghostfolioFearAndGreedIndexDataSourceStocks, - symbol: ghostfolioFearAndGreedIndexSymbol + symbol: ghostfolioFearAndGreedIndexSymbolStocks } }); diff --git a/apps/client/src/app/components/home-market/home-market.component.ts b/apps/client/src/app/components/home-market/home-market.component.ts index dd0c38cea..40b8f145b 100644 --- a/apps/client/src/app/components/home-market/home-market.component.ts +++ b/apps/client/src/app/components/home-market/home-market.component.ts @@ -1,6 +1,6 @@ import { GfFearAndGreedIndexComponent } from '@ghostfolio/client/components/fear-and-greed-index/fear-and-greed-index.component'; import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { ghostfolioFearAndGreedIndexSymbol } from '@ghostfolio/common/config'; +import { ghostfolioFearAndGreedIndexSymbolStocks } from '@ghostfolio/common/config'; import { resetHours } from '@ghostfolio/common/helper'; import { Benchmark, @@ -86,7 +86,7 @@ export class GfHomeMarketComponent implements OnInit { .fetchSymbolItem({ dataSource: this.info.fearAndGreedDataSource, includeHistoricalData: this.numberOfDays, - symbol: ghostfolioFearAndGreedIndexSymbol + symbol: ghostfolioFearAndGreedIndexSymbolStocks }) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(({ historicalData, marketPrice }) => { diff --git a/libs/common/src/lib/config.ts b/libs/common/src/lib/config.ts index e21271ca1..40412d627 100644 --- a/libs/common/src/lib/config.ts +++ b/libs/common/src/lib/config.ts @@ -5,11 +5,13 @@ import ms from 'ms'; import { ColorScheme, DateRange } from './types'; export const ghostfolioPrefix = 'GF'; + +/* @deprecated */ export const ghostfolioScraperApiSymbolPrefix = `_${ghostfolioPrefix}_`; + export const ghostfolioFearAndGreedIndexDataSourceCryptocurrencies = DataSource.MANUAL; export const ghostfolioFearAndGreedIndexDataSourceStocks = DataSource.RAPID_API; -export const ghostfolioFearAndGreedIndexSymbol = `${ghostfolioScraperApiSymbolPrefix}FEAR_AND_GREED_INDEX`; export const ghostfolioFearAndGreedIndexSymbolCryptocurrencies = `${ghostfolioPrefix}_FEAR_AND_GREED_INDEX_CRYPTOCURRENCIES`; export const ghostfolioFearAndGreedIndexSymbolStocks = `${ghostfolioPrefix}_FEAR_AND_GREED_INDEX_STOCKS`; diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 5bd6671f2..3219ed553 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -38,7 +38,6 @@ import { DEFAULT_CURRENCY, DEFAULT_LOCALE, DERIVED_CURRENCIES, - ghostfolioFearAndGreedIndexSymbol, ghostfolioFearAndGreedIndexSymbolCryptocurrencies, ghostfolioFearAndGreedIndexSymbolStocks, ghostfolioScraperApiSymbolPrefix @@ -157,7 +156,6 @@ export function canDeleteAssetProfile({ !isBenchmark && !isDerivedCurrency(getCurrencyFromSymbol(symbol)) && !isRootCurrency(getCurrencyFromSymbol(symbol)) && - symbol !== ghostfolioFearAndGreedIndexSymbol && symbol !== ghostfolioFearAndGreedIndexSymbolCryptocurrencies && symbol !== ghostfolioFearAndGreedIndexSymbolStocks && watchedByCount === 0 From 46d740e381c6852145fbe43005120ceccb5b5c36 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Mon, 22 Jun 2026 21:34:06 +0200 Subject: [PATCH 20/60] Task/consolidate exchange rates gathering together with hourly market data (#7095) * Consolidate exchange rates gathering with hourly market data * Update changelog --- CHANGELOG.md | 1 + apps/api/src/app/admin/admin.controller.ts | 4 +- apps/api/src/services/cron/cron.module.ts | 6 - apps/api/src/services/cron/cron.service.ts | 11 +- .../data-gathering/data-gathering.service.ts | 178 +++++++++--------- .../admin-market-data.component.ts | 22 +-- .../admin-market-data/admin-market-data.html | 2 +- libs/ui/src/lib/services/admin.service.ts | 8 +- 8 files changed, 113 insertions(+), 119 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bd562978..8568c74f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Consolidated the exchange rates to be gathered with hourly market data - Improved the language localization for German (`de`) - Upgraded `@openrouter/ai-sdk-provider` from version `2.9.0` to `2.9.1` - Upgraded `undici` from version `7.24.4` to `8.5.0` diff --git a/apps/api/src/app/admin/admin.controller.ts b/apps/api/src/app/admin/admin.controller.ts index 9eb045fba..a6f6e8b6f 100644 --- a/apps/api/src/app/admin/admin.controller.ts +++ b/apps/api/src/app/admin/admin.controller.ts @@ -86,8 +86,8 @@ export class AdminController { @HasPermission(permissions.accessAdminControl) @Post('gather') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) - public async gather7Days(): Promise { - this.dataGatheringService.gather7Days(); + public async gatherRecentMarketData(): Promise { + this.dataGatheringService.gatherRecentMarketData(); } @HasPermission(permissions.accessAdminControl) diff --git a/apps/api/src/services/cron/cron.module.ts b/apps/api/src/services/cron/cron.module.ts index 3d999e397..bc14990c1 100644 --- a/apps/api/src/services/cron/cron.module.ts +++ b/apps/api/src/services/cron/cron.module.ts @@ -2,8 +2,6 @@ import { UserModule } from '@ghostfolio/api/app/user/user.module'; import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ConfigurationModule } from '@ghostfolio/api/services/configuration/configuration.module'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; -import { ExchangeRateDataModule } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.module'; -import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PropertyModule } from '@ghostfolio/api/services/property/property.module'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { DataGatheringQueueModule } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.module'; @@ -21,7 +19,6 @@ import { CronService } from './cron.service'; imports: [ ConfigurationModule, DataGatheringQueueModule, - ExchangeRateDataModule, PropertyModule, StatisticsGatheringQueueModule, TwitterBotModule, @@ -32,7 +29,6 @@ import { CronService } from './cron.service'; inject: [ ConfigurationService, DataGatheringService, - ExchangeRateDataService, PropertyService, StatisticsGatheringService, TwitterBotService, @@ -42,7 +38,6 @@ import { CronService } from './cron.service'; useFactory: ( configurationService: ConfigurationService, dataGatheringService: DataGatheringService, - exchangeRateDataService: ExchangeRateDataService, propertyService: PropertyService, statisticsGatheringService: StatisticsGatheringService, twitterBotService: TwitterBotService, @@ -57,7 +52,6 @@ import { CronService } from './cron.service'; return new CronService( configurationService, dataGatheringService, - exchangeRateDataService, propertyService, statisticsGatheringService, twitterBotService, diff --git a/apps/api/src/services/cron/cron.service.ts b/apps/api/src/services/cron/cron.service.ts index 7299cbbf4..b3e60ae59 100644 --- a/apps/api/src/services/cron/cron.service.ts +++ b/apps/api/src/services/cron/cron.service.ts @@ -1,6 +1,5 @@ import { UserService } from '@ghostfolio/api/app/user/user.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; -import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; import { PropertyService } from '@ghostfolio/api/services/property/property.service'; import { DataGatheringService } from '@ghostfolio/api/services/queues/data-gathering/data-gathering.service'; import { StatisticsGatheringService } from '@ghostfolio/api/services/queues/statistics-gathering/statistics-gathering.service'; @@ -24,7 +23,6 @@ export class CronService { public constructor( private readonly configurationService: ConfigurationService, private readonly dataGatheringService: DataGatheringService, - private readonly exchangeRateDataService: ExchangeRateDataService, private readonly propertyService: PropertyService, private readonly statisticsGatheringService: StatisticsGatheringService, private readonly twitterBotService: TwitterBotService, @@ -41,16 +39,11 @@ export class CronService { @Cron(CronService.EVERY_HOUR_AT_RANDOM_MINUTE) public async runEveryHourAtRandomMinute() { if (await this.isDataGatheringEnabled()) { - await this.dataGatheringService.gather7Days(); - await this.dataGatheringService.gatherHourlySymbols(); + await this.dataGatheringService.gatherHourlyMarketData(); + await this.dataGatheringService.gatherRecentMarketData(); } } - @Cron(CronExpression.EVERY_12_HOURS) - public async runEveryTwelveHours() { - await this.exchangeRateDataService.loadCurrencies(); - } - @Cron(CronExpression.EVERY_DAY_AT_5PM) public async runEveryDayAtFivePm() { if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts index ff65ebc83..1a2fa720e 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.service.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.service.ts @@ -69,91 +69,6 @@ export class DataGatheringService { return this.dataGatheringQueue.addBulk(jobs); } - public async gather7Days() { - await this.gatherSymbols({ - dataGatheringItems: await this.getCurrencies7D(), - priority: DATA_GATHERING_QUEUE_PRIORITY_HIGH - }); - - await this.gatherSymbols({ - dataGatheringItems: await this.getSymbols7D({ - withUserSubscription: true - }), - priority: DATA_GATHERING_QUEUE_PRIORITY_MEDIUM - }); - - await this.gatherSymbols({ - dataGatheringItems: await this.getSymbols7D({ - withUserSubscription: false - }), - priority: DATA_GATHERING_QUEUE_PRIORITY_LOW - }); - } - - public async gatherMax() { - const dataGatheringItems = await this.getSymbolsMax(); - await this.gatherSymbols({ - dataGatheringItems, - priority: DATA_GATHERING_QUEUE_PRIORITY_LOW - }); - } - - public async gatherSymbol({ dataSource, date, symbol }: DataGatheringItem) { - const dataGatheringItems = (await this.getSymbolsMax()) - .filter((dataGatheringItem) => { - return ( - dataGatheringItem.dataSource === dataSource && - dataGatheringItem.symbol === symbol - ); - }) - .map((item) => ({ - ...item, - date: date ?? item.date - })); - - await this.gatherSymbols({ - dataGatheringItems, - force: true, - priority: DATA_GATHERING_QUEUE_PRIORITY_HIGH - }); - } - - public async gatherSymbolForDate({ - dataSource, - date, - symbol - }: { date: Date } & AssetProfileIdentifier) { - try { - const historicalData = await this.dataProviderService.getHistoricalRaw({ - assetProfileIdentifiers: [{ dataSource, symbol }], - from: date, - to: date - }); - - const marketPrice = - historicalData[getAssetProfileIdentifier({ dataSource, symbol })][ - format(date, DATE_FORMAT) - ].marketPrice; - - if (marketPrice) { - return await this.prismaService.marketData.upsert({ - create: { - dataSource, - date, - marketPrice, - symbol - }, - update: { marketPrice }, - where: { dataSource_date_symbol: { dataSource, date, symbol } } - }); - } - } catch (error) { - this.logger.error(error); - } finally { - return undefined; - } - } - public async gatherAssetProfiles( aAssetProfileIdentifiers?: AssetProfileIdentifier[] ) { @@ -282,7 +197,13 @@ export class DataGatheringService { } } - public async gatherHourlySymbols() { + public async gatherHourlyMarketData() { + try { + await this.exchangeRateDataService.loadCurrencies(); + } catch (error) { + this.logger.error('Could not gather exchange rates', error); + } + const assetProfileIdentifiers = await this.getHourlyAssetProfileIdentifiers(); @@ -322,6 +243,91 @@ export class DataGatheringService { } } + public async gatherMax() { + const dataGatheringItems = await this.getSymbolsMax(); + await this.gatherSymbols({ + dataGatheringItems, + priority: DATA_GATHERING_QUEUE_PRIORITY_LOW + }); + } + + public async gatherRecentMarketData() { + await this.gatherSymbols({ + dataGatheringItems: await this.getCurrencies7D(), + priority: DATA_GATHERING_QUEUE_PRIORITY_HIGH + }); + + await this.gatherSymbols({ + dataGatheringItems: await this.getSymbols7D({ + withUserSubscription: true + }), + priority: DATA_GATHERING_QUEUE_PRIORITY_MEDIUM + }); + + await this.gatherSymbols({ + dataGatheringItems: await this.getSymbols7D({ + withUserSubscription: false + }), + priority: DATA_GATHERING_QUEUE_PRIORITY_LOW + }); + } + + public async gatherSymbol({ dataSource, date, symbol }: DataGatheringItem) { + const dataGatheringItems = (await this.getSymbolsMax()) + .filter((dataGatheringItem) => { + return ( + dataGatheringItem.dataSource === dataSource && + dataGatheringItem.symbol === symbol + ); + }) + .map((item) => ({ + ...item, + date: date ?? item.date + })); + + await this.gatherSymbols({ + dataGatheringItems, + force: true, + priority: DATA_GATHERING_QUEUE_PRIORITY_HIGH + }); + } + + public async gatherSymbolForDate({ + dataSource, + date, + symbol + }: { date: Date } & AssetProfileIdentifier) { + try { + const historicalData = await this.dataProviderService.getHistoricalRaw({ + assetProfileIdentifiers: [{ dataSource, symbol }], + from: date, + to: date + }); + + const marketPrice = + historicalData[getAssetProfileIdentifier({ dataSource, symbol })][ + format(date, DATE_FORMAT) + ].marketPrice; + + if (marketPrice) { + return await this.prismaService.marketData.upsert({ + create: { + dataSource, + date, + marketPrice, + symbol + }, + update: { marketPrice }, + where: { dataSource_date_symbol: { dataSource, date, symbol } } + }); + } + } catch (error) { + this.logger.error(error); + } finally { + return undefined; + } + } + public async gatherSymbols({ dataGatheringItems, force = false, diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts index 511d6df98..ae0f8fda2 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.component.ts @@ -305,17 +305,6 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit { ); } - protected onGather7Days() { - this.adminService - .gather7Days() - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(() => { - setTimeout(() => { - window.location.reload(); - }, 300); - }); - } - protected onGatherMax() { this.adminService .gatherMax() @@ -334,6 +323,17 @@ export class GfAdminMarketDataComponent implements AfterViewInit, OnInit { .subscribe(); } + protected onGatherRecentMarketData() { + this.adminService + .gatherRecentMarketData() + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => { + setTimeout(() => { + window.location.reload(); + }, 300); + }); + } + protected onOpenAssetProfileDialog({ dataSource, symbol diff --git a/apps/client/src/app/components/admin-market-data/admin-market-data.html b/apps/client/src/app/components/admin-market-data/admin-market-data.html index e86e3e631..270722a4b 100644 --- a/apps/client/src/app/components/admin-market-data/admin-market-data.html +++ b/apps/client/src/app/components/admin-market-data/admin-market-data.html @@ -220,7 +220,7 @@ class="no-max-width" xPosition="before" > -
Ghostfolio vs {{ - product2.name + product2().name }} comparison table
Ghostfolio - {{ product2.name }} + {{ product2().name }}
{{ product1.slogan }}{{ product2.slogan }}{{ product1().slogan }}{{ product2().slogan }}
Founded{{ product1.founded }}{{ product2.founded }}{{ product1().founded }}{{ product2().founded }}
Origin{{ product1.origin }}{{ product2.origin }}{{ product1().origin }}{{ product2().origin }}
Region @for ( - region of product1.regions; + region of product1().regions; track region; let isLast = $last ) { @@ -96,7 +96,7 @@ @for ( - region of product2.regions; + region of product2().regions; track region; let isLast = $last ) { @@ -110,7 +110,7 @@ @for ( - language of product1.languages; + language of product1().languages; track language; let isLast = $last ) { @@ -119,7 +119,7 @@ @for ( - language of product2.languages; + language of product2().languages; track language; let isLast = $last ) { @@ -132,35 +132,35 @@ Open Source Software - @if (product1.isOpenSource) { + @if (product1().isOpenSource) { ✅ Yes } @else { ❌ No } - @if (product2.isOpenSource) { + @if (product2().isOpenSource) { ✅ Yes } @else { ❌ No } @@ -171,35 +171,35 @@ Self-Hosting - @if (product1.hasSelfHostingAbility === true) { + @if (product1().hasSelfHostingAbility === true) { ✅ Yes - } @else if (product1.hasSelfHostingAbility === false) { + } @else if (product1().hasSelfHostingAbility === false) { ❌ No } - @if (product2.hasSelfHostingAbility === true) { + @if (product2().hasSelfHostingAbility === true) { ✅ Yes - } @else if (product2.hasSelfHostingAbility === false) { + } @else if (product2().hasSelfHostingAbility === false) { ❌ No } @@ -210,35 +210,35 @@ Use anonymously - @if (product1.useAnonymously === true) { + @if (product1().useAnonymously === true) { ✅ Yes - } @else if (product1.useAnonymously === false) { + } @else if (product1().useAnonymously === false) { ❌ No } - @if (product2.useAnonymously === true) { + @if (product2().useAnonymously === true) { ✅ Yes - } @else if (product2.useAnonymously === false) { + } @else if (product2().useAnonymously === false) { ❌ No } @@ -249,35 +249,35 @@ Free Plan - @if (product1.hasFreePlan === true) { + @if (product1().hasFreePlan === true) { ✅ Yes - } @else if (product1.hasFreePlan === false) { + } @else if (product1().hasFreePlan === false) { ❌ No } - @if (product2.hasFreePlan === true) { + @if (product2().hasFreePlan === true) { ✅ Yes - } @else if (product2.hasFreePlan === false) { + } @else if (product2().hasFreePlan === false) { ❌ No } @@ -286,22 +286,22 @@
Pricing - Starting from ${{ price }} / + Starting from ${{ price() }} / year - @if (product2.pricingPerYear) { + @if (product2().pricingPerYear) { Starting from - {{ product2.pricingPerYear }} / + {{ product2().pricingPerYear }} / year }
Notes{{ product1.note }}{{ product2.note }}{{ product1().note }}{{ product2().note }}
+ + diff --git a/apps/client/src/app/components/admin-platform/admin-platform.component.ts b/apps/client/src/app/components/admin-platform/admin-platform.component.ts index 26e6c2b1e..727585478 100644 --- a/apps/client/src/app/components/admin-platform/admin-platform.component.ts +++ b/apps/client/src/app/components/admin-platform/admin-platform.component.ts @@ -1,4 +1,5 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { CreatePlatformDto, UpdatePlatformDto } from '@ghostfolio/common/dtos'; import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getLocale, getLowercase } from '@ghostfolio/common/helper'; @@ -22,6 +23,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; +import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatSort, MatSortModule } from '@angular/material/sort'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; @@ -46,6 +48,7 @@ import { CreateOrUpdatePlatformDialogParams } from './create-or-update-platform- IonIcon, MatButtonModule, MatMenuModule, + MatPaginatorModule, MatSortModule, MatTableModule, RouterModule @@ -59,11 +62,13 @@ export class GfAdminPlatformComponent implements OnInit { protected dataSource = new MatTableDataSource(); protected readonly displayedColumns = ['name', 'url', 'accounts', 'actions']; + protected readonly pageSize = DEFAULT_PAGE_SIZE; protected platforms: Platform[]; private readonly deviceType = computed( () => this.deviceDetectorService.deviceInfo().deviceType ); + private readonly paginator = viewChild.required(MatPaginator); private readonly sort = viewChild.required(MatSort); private readonly adminService = inject(AdminService); @@ -145,6 +150,7 @@ export class GfAdminPlatformComponent implements OnInit { this.platforms = platforms; this.dataSource = new MatTableDataSource(platforms); + this.dataSource.paginator = this.paginator(); this.dataSource.sort = this.sort(); this.dataSource.sortingDataAccessor = getLowercase; diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.html b/apps/client/src/app/components/admin-tag/admin-tag.component.html index 3c125d5c0..a84cbb283 100644 --- a/apps/client/src/app/components/admin-tag/admin-tag.component.html +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.html @@ -89,3 +89,9 @@ + + diff --git a/apps/client/src/app/components/admin-tag/admin-tag.component.ts b/apps/client/src/app/components/admin-tag/admin-tag.component.ts index fcb901707..7bd69c964 100644 --- a/apps/client/src/app/components/admin-tag/admin-tag.component.ts +++ b/apps/client/src/app/components/admin-tag/admin-tag.component.ts @@ -1,4 +1,5 @@ import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { DEFAULT_PAGE_SIZE } from '@ghostfolio/common/config'; import { CreateTagDto, UpdateTagDto } from '@ghostfolio/common/dtos'; import { ConfirmationDialogType } from '@ghostfolio/common/enums'; import { getLocale, getLowercase } from '@ghostfolio/common/helper'; @@ -21,6 +22,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatMenuModule } from '@angular/material/menu'; +import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator'; import { MatSort, MatSortModule } from '@angular/material/sort'; import { MatTableDataSource, MatTableModule } from '@angular/material/table'; import { ActivatedRoute, Router, RouterModule } from '@angular/router'; @@ -44,6 +46,7 @@ import { CreateOrUpdateTagDialogParams } from './create-or-update-tag-dialog/int IonIcon, MatButtonModule, MatMenuModule, + MatPaginatorModule, MatSortModule, MatTableModule, RouterModule @@ -62,11 +65,13 @@ export class GfAdminTagComponent implements OnInit { 'activities', 'actions' ]; + protected readonly pageSize = DEFAULT_PAGE_SIZE; protected tags: Tag[]; private readonly deviceType = computed( () => this.deviceDetectorService.deviceInfo().deviceType ); + private readonly paginator = viewChild.required(MatPaginator); private readonly sort = viewChild.required(MatSort); private readonly changeDetectorRef = inject(ChangeDetectorRef); @@ -147,6 +152,7 @@ export class GfAdminTagComponent implements OnInit { this.tags = tags; this.dataSource = new MatTableDataSource(this.tags); + this.dataSource.paginator = this.paginator(); this.dataSource.sort = this.sort(); this.dataSource.sortingDataAccessor = getLowercase; From 0d5c20622647fcdecabee2680b3b6a71a494b41b Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 18:19:33 +0200 Subject: [PATCH 40/60] Bugfix/log context formatting in performance logging service (#7121) * Fix log context formatting * Update changelog --- CHANGELOG.md | 1 + .../performance-logging/performance-logging.service.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47c43d38f..4f019b7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed an issue with hourly market data updates not refreshing prices for asset profiles with `MANUAL` data source +- Fixed an issue with the log context formatting in the performance logging service ## 3.15.1 - 2026-06-23 diff --git a/apps/api/src/interceptors/performance-logging/performance-logging.service.ts b/apps/api/src/interceptors/performance-logging/performance-logging.service.ts index a07783cd9..b2ea03c37 100644 --- a/apps/api/src/interceptors/performance-logging/performance-logging.service.ts +++ b/apps/api/src/interceptors/performance-logging/performance-logging.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class PerformanceLoggingService { - private readonly logger = new Logger(PerformanceLoggingService.name); + private readonly logger = new Logger(); public logPerformance({ className, From 6d96f75aca218338e150ad4170bdd32e0d63d09c Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 19:00:10 +0200 Subject: [PATCH 41/60] Feature/extend asset profile details dialog with copy to clipboard buttons for ISIN and symbol (#7124) * Extend dialog with copy-to-clipboard button for ISIN number and symbol * Update changelog --- CHANGELOG.md | 2 ++ .../asset-profile-dialog/asset-profile-dialog.html | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f019b7f3..00c5a90fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added pagination to the platform management of the admin control panel - Added pagination to the tag management of the admin control panel +- Extended the asset profile details dialog of the admin control panel with a copy-to-clipboard button for the ISIN number +- Extended the asset profile details dialog of the admin control panel with a copy-to-clipboard button for the symbol ### Fixed diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html index f00c279a0..9b69ef6fc 100644 --- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html +++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html @@ -165,7 +165,11 @@
} @else {
- Symbol
@@ -202,6 +206,7 @@
ISIN Date: Wed, 24 Jun 2026 19:01:15 +0200 Subject: [PATCH 42/60] Feature/extend user account settings with copy-to-clipboard button for user id (#7125) * Extend settings with copy-to-clipboard button for user id * Update changelog --- CHANGELOG.md | 1 + .../user-account-settings.component.ts | 2 ++ .../user-account-settings/user-account-settings.html | 8 +++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c5a90fc..04a4d896a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Extended the user account settings with a copy-to-clipboard button for the user id - Added pagination to the platform management of the admin control panel - Added pagination to the tag management of the admin control panel - Extended the asset profile details dialog of the admin control panel with a copy-to-clipboard button for the ISIN number diff --git a/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts b/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts index 72bbfc2c6..0101bb90a 100644 --- a/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts +++ b/apps/client/src/app/components/user-account-settings/user-account-settings.component.ts @@ -12,6 +12,7 @@ import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { internalRoutes } from '@ghostfolio/common/routes/routes'; import { NotificationService } from '@ghostfolio/ui/notifications'; import { DataService } from '@ghostfolio/ui/services'; +import { GfValueComponent } from '@ghostfolio/ui/value'; import { ChangeDetectionStrategy, @@ -51,6 +52,7 @@ import { catchError } from 'rxjs/operators'; changeDetection: ChangeDetectionStrategy.OnPush, imports: [ FormsModule, + GfValueComponent, IonIcon, MatButtonModule, MatCardModule, diff --git a/apps/client/src/app/components/user-account-settings/user-account-settings.html b/apps/client/src/app/components/user-account-settings/user-account-settings.html index f646ef0fd..e4e9777de 100644 --- a/apps/client/src/app/components/user-account-settings/user-account-settings.html +++ b/apps/client/src/app/components/user-account-settings/user-account-settings.html @@ -260,7 +260,13 @@
Ghostfolio User ID
-
{{ user?.id }}
+
+ +
From 95b0318f8e73370b459e761aa333cf8a9c0eb003 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 19:04:36 +0200 Subject: [PATCH 43/60] Task/improve throughput of market data gathering queue by applying rate limit per data source (#7128) * Improve throughput by applying rate limit per data source * Update changelog --- CHANGELOG.md | 4 ++++ .../services/queues/data-gathering/data-gathering.module.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04a4d896a..df2f6ba9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Extended the asset profile details dialog of the admin control panel with a copy-to-clipboard button for the ISIN number - Extended the asset profile details dialog of the admin control panel with a copy-to-clipboard button for the symbol +### Changed + +- Improved the throughput of the market data gathering queue by applying the rate limit per data source + ### Fixed - Fixed an issue with hourly market data updates not refreshing prices for asset profiles with `MANUAL` data source diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.module.ts b/apps/api/src/services/queues/data-gathering/data-gathering.module.ts index 5ac6c40c0..7258c034c 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.module.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.module.ts @@ -29,6 +29,7 @@ import { DataGatheringProcessor } from './data-gathering.processor'; BullModule.registerQueue({ limiter: { duration: ms('4 seconds'), + groupKey: 'dataSource', max: 1 }, name: DATA_GATHERING_QUEUE From 3c758b90e2e81fa5e1ca4294c75bd0aff57340fa Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 19:17:00 +0200 Subject: [PATCH 44/60] Task/decrease rate limiter duration of market data gathering queue (#7129) * Decrease rate limiter duration * Update changelog --- CHANGELOG.md | 1 + .../src/services/queues/data-gathering/data-gathering.module.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df2f6ba9e..88ea7dd26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Improved the throughput of the market data gathering queue by applying the rate limit per data source +- Decreased the rate limiter duration of the market data gathering queue jobs from 4 to 3 seconds ### Fixed diff --git a/apps/api/src/services/queues/data-gathering/data-gathering.module.ts b/apps/api/src/services/queues/data-gathering/data-gathering.module.ts index 7258c034c..d66411797 100644 --- a/apps/api/src/services/queues/data-gathering/data-gathering.module.ts +++ b/apps/api/src/services/queues/data-gathering/data-gathering.module.ts @@ -28,7 +28,7 @@ import { DataGatheringProcessor } from './data-gathering.processor'; }), BullModule.registerQueue({ limiter: { - duration: ms('4 seconds'), + duration: ms('3 seconds'), groupKey: 'dataSource', max: 1 }, From e8c0e62dbb63cdfce6d426db96f5f0a591be5d1f Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 19:17:23 +0200 Subject: [PATCH 45/60] Task/upgrade simplewebauthn to version 13.3 (#7115) * Upgrade simplewebauthn to version 13.3 * Update changelog --- CHANGELOG.md | 4 ++++ package-lock.json | 28 ++++++++++++++-------------- package.json | 4 ++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88ea7dd26..f8a0ca796 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the throughput of the market data gathering queue by applying the rate limit per data source - Decreased the rate limiter duration of the market data gathering queue jobs from 4 to 3 seconds +### Changed + +- Upgraded `@simplewebauthn/browser` and `@simplewebauthn/server` from version `13.2.2` to `13.3` + ### Fixed - Fixed an issue with hourly market data updates not refreshing prices for asset profiles with `MANUAL` data source diff --git a/package-lock.json b/package-lock.json index e40bcbe1c..bb9e62dbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,8 +43,8 @@ "@openrouter/ai-sdk-provider": "2.9.1", "@prisma/adapter-pg": "7.8.0", "@prisma/client": "7.8.0", - "@simplewebauthn/browser": "13.2.2", - "@simplewebauthn/server": "13.2.2", + "@simplewebauthn/browser": "13.3.0", + "@simplewebauthn/server": "13.3.1", "ai": "6.0.174", "alphavantage": "2.2.0", "big.js": "7.0.1", @@ -13296,25 +13296,25 @@ } }, "node_modules/@simplewebauthn/browser": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.2.2.tgz", - "integrity": "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.3.0.tgz", + "integrity": "sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==", "license": "MIT" }, "node_modules/@simplewebauthn/server": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.2.2.tgz", - "integrity": "sha512-HcWLW28yTMGXpwE9VLx9J+N2KEUaELadLrkPEEI9tpI5la70xNEVEsu/C+m3u7uoq4FulLqZQhgBCzR9IZhFpA==", + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.3.1.tgz", + "integrity": "sha512-GV/oM/qeycWn8p42JZIMJBsXWQcNFg+nJFzeQTnMA4gN8mXg0+HZFWJerHg8ZN/zlveMS3iV1wzuFpOVWS/46w==", "license": "MIT", "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", - "@peculiar/asn1-android": "^2.3.10", - "@peculiar/asn1-ecc": "^2.3.8", - "@peculiar/asn1-rsa": "^2.3.8", - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/asn1-x509": "^2.3.8", - "@peculiar/x509": "^1.13.0" + "@peculiar/asn1-android": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.1", + "@peculiar/asn1-rsa": "^2.6.1", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "@peculiar/x509": "^1.14.3" }, "engines": { "node": ">=20.0.0" diff --git a/package.json b/package.json index df644f017..7371a9187 100644 --- a/package.json +++ b/package.json @@ -87,8 +87,8 @@ "@openrouter/ai-sdk-provider": "2.9.1", "@prisma/adapter-pg": "7.8.0", "@prisma/client": "7.8.0", - "@simplewebauthn/browser": "13.2.2", - "@simplewebauthn/server": "13.2.2", + "@simplewebauthn/browser": "13.3.0", + "@simplewebauthn/server": "13.3.1", "ai": "6.0.174", "alphavantage": "2.2.0", "big.js": "7.0.1", From 981f21425f4e565c7a4215a5bf606a378558e680 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 19:43:34 +0200 Subject: [PATCH 46/60] Task/upgrade @types/big.js to version 7.0.0 (#7098) Upgrade @types/big.js to version 7.0.0 --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index bb9e62dbd..7cc890099 100644 --- a/package-lock.json +++ b/package-lock.json @@ -129,7 +129,7 @@ "@storybook/addon-themes": "10.1.10", "@storybook/angular": "10.1.10", "@trivago/prettier-plugin-sort-imports": "6.0.2", - "@types/big.js": "6.2.2", + "@types/big.js": "7.0.0", "@types/cookie-parser": "1.4.10", "@types/fast-redact": "3.0.4", "@types/google-spreadsheet": "3.1.5", @@ -14020,9 +14020,9 @@ } }, "node_modules/@types/big.js": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@types/big.js/-/big.js-6.2.2.tgz", - "integrity": "sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/big.js/-/big.js-7.0.0.tgz", + "integrity": "sha512-WfAGp7IbJvyB8EmWK4tJD24rJRAL6uVbw3LV/hJntFNam+os9KWKj0PzXo8rRRpjupYK8U0M8FoBB8dBhWF2dg==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 7371a9187..fb1976570 100644 --- a/package.json +++ b/package.json @@ -173,7 +173,7 @@ "@storybook/addon-themes": "10.1.10", "@storybook/angular": "10.1.10", "@trivago/prettier-plugin-sort-imports": "6.0.2", - "@types/big.js": "6.2.2", + "@types/big.js": "7.0.0", "@types/cookie-parser": "1.4.10", "@types/fast-redact": "3.0.4", "@types/google-spreadsheet": "3.1.5", From 9b86a9a73f2fe00509de2d74a51f3081a36cfd6a Mon Sep 17 00:00:00 2001 From: Akash Negi <95514575+AkashNegi1@users.noreply.github.com> Date: Wed, 24 Jun 2026 23:44:14 +0530 Subject: [PATCH 47/60] Task/remove deprecated SymbolProfile from PortfolioHoldingResponse (#7094) * Remove deprecated SymbolProfile * Update changelog --- CHANGELOG.md | 4 +- .../src/app/portfolio/portfolio.service.ts | 1 - .../holding-detail-dialog.component.ts | 52 +++++++++++------- .../holding-detail-dialog.html | 54 +++++++++---------- .../portfolio-holding-response.interface.ts | 4 -- 5 files changed, 60 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8a0ca796..76dbe113a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,9 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the throughput of the market data gathering queue by applying the rate limit per data source - Decreased the rate limiter duration of the market data gathering queue jobs from 4 to 3 seconds - -### Changed - +- Removed the deprecated `SymbolProfile` field from the endpoint `GET api/v1/portfolio/holding/:dataSource/:symbol` - Upgraded `@simplewebauthn/browser` and `@simplewebauthn/server` from version `13.2.2` to `13.3` ### Fixed diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 77cbb5b49..41697b346 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -957,7 +957,6 @@ export class PortfolioService { marketPrice, marketPriceMax, marketPriceMin, - SymbolProfile, tags, assetProfile: { assetClass: SymbolProfile.assetClass, diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts index e14c638be..6fa3a4853 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.component.ts @@ -116,6 +116,19 @@ export class GfHoldingDetailDialogComponent implements OnInit { protected accounts: Account[]; protected activitiesCount: number; protected assetClass: string; + protected assetProfile: Pick< + EnhancedSymbolProfile, + | 'assetClass' + | 'assetSubClass' + | 'countries' + | 'currency' + | 'dataSource' + | 'isin' + | 'name' + | 'sectors' + | 'symbol' + | 'userId' + >; protected assetSubClass: string; protected averagePrice: number; protected averagePricePrecision = 2; @@ -164,7 +177,6 @@ export class GfHoldingDetailDialogComponent implements OnInit { }; protected sortColumn = 'date'; protected sortDirection: SortDirection = 'desc'; - protected SymbolProfile: EnhancedSymbolProfile; protected tagsAvailable: Tag[]; protected readonly translate = translate; protected user: User; @@ -266,6 +278,7 @@ export class GfHoldingDetailDialogComponent implements OnInit { .subscribe( ({ activitiesCount, + assetProfile, averagePrice, dataProviderInfo, dateOfFirstActivity, @@ -280,11 +293,11 @@ export class GfHoldingDetailDialogComponent implements OnInit { netPerformancePercentWithCurrencyEffect, netPerformanceWithCurrencyEffect, quantity, - SymbolProfile, tags, value }) => { this.activitiesCount = activitiesCount; + this.assetProfile = assetProfile; this.averagePrice = averagePrice; if ( @@ -322,8 +335,8 @@ export class GfHoldingDetailDialogComponent implements OnInit { this.user?.permissions, permissions.readMarketDataOfOwnAssetProfile ) && - SymbolProfile?.dataSource === 'MANUAL' && - SymbolProfile?.userId === this.user?.id; + assetProfile?.dataSource === 'MANUAL' && + assetProfile?.userId === this.user?.id; this.historicalDataItems = historicalData.map( ({ averagePrice, date, marketPrice }) => { @@ -402,7 +415,7 @@ export class GfHoldingDetailDialogComponent implements OnInit { if (Number.isInteger(this.quantity)) { this.quantityPrecision = 0; - } else if (SymbolProfile?.assetSubClass === 'CRYPTOCURRENCY') { + } else if (assetProfile?.assetSubClass === 'CRYPTOCURRENCY') { if (this.quantity < 10) { this.quantityPrecision = 8; } else if (this.quantity < 1000) { @@ -413,7 +426,6 @@ export class GfHoldingDetailDialogComponent implements OnInit { } this.sectors = {}; - this.SymbolProfile = SymbolProfile; this.tags = tags.map((tag) => { return { @@ -427,21 +439,21 @@ export class GfHoldingDetailDialogComponent implements OnInit { this.value = value; const reportDataGlitchSubject = `Ghostfolio Data Glitch Report${ - this.SymbolProfile?.symbol ? ` (${this.SymbolProfile.symbol})` : '' + this.assetProfile?.symbol ? ` (${this.assetProfile.symbol})` : '' }`; - this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=${reportDataGlitchSubject}&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${this.SymbolProfile?.symbol}%0DData Source: ${this.SymbolProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`; + this.reportDataGlitchMail = `mailto:hi@ghostfol.io?Subject=${reportDataGlitchSubject}&body=Hello%0D%0DI would like to report a data glitch for%0D%0DSymbol: ${this.assetProfile?.symbol}%0DData Source: ${this.assetProfile?.dataSource}%0D%0DAdditional notes:%0D%0DCan you please take a look?%0D%0DKind regards`; - if (this.SymbolProfile?.assetClass) { - this.assetClass = translate(this.SymbolProfile?.assetClass); + if (this.assetProfile?.assetClass) { + this.assetClass = translate(this.assetProfile?.assetClass); } - if (this.SymbolProfile?.assetSubClass) { - this.assetSubClass = translate(this.SymbolProfile?.assetSubClass); + if (this.assetProfile?.assetSubClass) { + this.assetSubClass = translate(this.assetProfile?.assetSubClass); } - if (this.SymbolProfile?.countries?.length > 0) { - for (const country of this.SymbolProfile.countries) { + if (this.assetProfile?.countries?.length > 0) { + for (const country of this.assetProfile.countries) { this.countries[country.code] = { name: getCountryName({ code: country.code }), value: country.weight @@ -449,8 +461,8 @@ export class GfHoldingDetailDialogComponent implements OnInit { } } - if (this.SymbolProfile?.sectors?.length > 0) { - for (const sector of this.SymbolProfile.sectors) { + if (this.assetProfile?.sectors?.length > 0) { + for (const sector of this.assetProfile.sectors) { this.sectors[sector.name] = { name: translate(sector.name), value: sector.weight @@ -570,12 +582,12 @@ export class GfHoldingDetailDialogComponent implements OnInit { const activity: CreateOrderDto = { accountId: this.accounts.length === 1 ? this.accounts[0].id : undefined, comment: undefined, - currency: this.SymbolProfile?.currency ?? '', - dataSource: this.SymbolProfile?.dataSource, + currency: this.assetProfile?.currency ?? '', + dataSource: this.assetProfile?.dataSource, date: today.toISOString(), fee: 0, quantity: this.quantity, - symbol: this.SymbolProfile?.symbol ?? '', + symbol: this.assetProfile?.symbol ?? '', tags: this.tags.map(({ id }) => { return id; }), @@ -606,7 +618,7 @@ export class GfHoldingDetailDialogComponent implements OnInit { .subscribe((data) => { downloadAsFile({ content: data, - fileName: `ghostfolio-export-${this.SymbolProfile?.symbol}-${format( + fileName: `ghostfolio-export-${this.assetProfile?.symbol}-${format( parseISO(data.meta.date), 'yyyyMMddHHmm' )}.json`, diff --git a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html index 0d5691874..ff1d19cc3 100644 --- a/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html +++ b/apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html @@ -1,7 +1,7 @@ @@ -24,11 +24,11 @@ [benchmarkDataItems]="benchmarkDataItems" [benchmarkLabel]="benchmarkLabel" [colorScheme]="data.colorScheme" - [currency]="SymbolProfile?.currency" + [currency]="assetProfile?.currency" [historicalDataItems]="historicalDataItems" [isAnimated]="true" [label]=" - isUUID(data.symbol) ? (SymbolProfile?.name ?? data.symbol) : data.symbol + isUUID(data.symbol) ? (assetProfile?.name ?? data.symbol) : data.symbol " [locale]="data.locale" [showGradient]="true" @@ -60,8 +60,8 @@ [value]="netPerformanceWithCurrencyEffect" > @if ( - SymbolProfile?.currency && - data.baseCurrency !== SymbolProfile?.currency + assetProfile?.currency && + data.baseCurrency !== assetProfile?.currency ) { Change with currency effect } @else { @@ -80,8 +80,8 @@ [value]="netPerformancePercentWithCurrencyEffect" > @if ( - SymbolProfile?.currency && - data.baseCurrency !== SymbolProfile?.currency + assetProfile?.currency && + data.baseCurrency !== assetProfile?.currency ) { Performance with currency effect } @else { @@ -96,7 +96,7 @@ [isCurrency]="true" [locale]="data.locale" [precision]="averagePricePrecision" - [unit]="SymbolProfile?.currency" + [unit]="assetProfile?.currency" [value]="averagePrice" >Average Unit Price @@ -108,7 +108,7 @@ [isCurrency]="true" [locale]="data.locale" [precision]="marketPricePrecision" - [unit]="SymbolProfile?.currency" + [unit]="assetProfile?.currency" [value]="marketPrice" >Market Price @@ -124,7 +124,7 @@ [isCurrency]="true" [locale]="data.locale" [precision]="marketPriceMinPrecision" - [unit]="SymbolProfile?.currency" + [unit]="assetProfile?.currency" [value]="marketPriceMin" >Minimum Price @@ -140,7 +140,7 @@ [isCurrency]="true" [locale]="data.locale" [precision]="marketPriceMaxPrecision" - [unit]="SymbolProfile?.currency" + [unit]="assetProfile?.currency" [value]="marketPriceMax" >Maximum Price @@ -250,23 +250,23 @@ >
@if ( - SymbolProfile?.countries?.length > 0 || - SymbolProfile?.sectors?.length > 0 + assetProfile?.countries?.length > 0 || + assetProfile?.sectors?.length > 0 ) { @if ( - SymbolProfile?.countries?.length === 1 && - SymbolProfile?.sectors?.length === 1 + assetProfile?.countries?.length === 1 && + assetProfile?.sectors?.length === 1 ) {
Sector
- @if (SymbolProfile?.countries?.length === 1) { + @if (assetProfile?.countries?.length === 1) {
CountrySymbol
@@ -322,8 +322,8 @@ ISIN
@@ -404,12 +404,12 @@
Market Data
@@ -462,8 +462,8 @@ mat-stroked-button [queryParams]="{ assetProfileDialog: true, - dataSource: SymbolProfile?.dataSource, - symbol: SymbolProfile?.symbol + dataSource: assetProfile?.dataSource, + symbol: assetProfile?.symbol }" [routerLink]="routerLinkAdminControlMarketData" (click)="onClose()" diff --git a/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts b/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts index 3b07666c9..075234e6a 100644 --- a/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/portfolio-holding-response.interface.ts @@ -44,10 +44,6 @@ export interface PortfolioHoldingResponse { netPerformanceWithCurrencyEffect: number; performances: Benchmark['performances']; quantity: number; - - /* @deprecated */ - SymbolProfile: EnhancedSymbolProfile; - tags: Tag[]; value: number; } From cb66e63ed4d1812c6f4dc6607e42d578425697ed Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Wed, 24 Jun 2026 20:15:50 +0200 Subject: [PATCH 48/60] Release 3.16.0 (#7131) --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76dbe113a..e220aa0ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 3.16.0 - 2026-06-24 ### Added diff --git a/package-lock.json b/package-lock.json index 7cc890099..49ac987ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "3.15.1", + "version": "3.16.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "3.15.1", + "version": "3.16.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index fb1976570..39f2ec046 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "3.15.1", + "version": "3.16.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", From af254383946eb9bc5b014f29921575e096e28dbc Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 25 Jun 2026 07:52:09 +0200 Subject: [PATCH 49/60] Task/upgrade prettier to version 3.8.4 (#7051) * Upgrade prettier to version 3.8.4 * Update changelog --- CHANGELOG.md | 6 ++++++ package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e220aa0ba..26f396788 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Changed + +- Upgraded `prettier` from version `3.8.3` to `3.8.4` + ## 3.16.0 - 2026-06-24 ### Added diff --git a/package-lock.json b/package-lock.json index 49ac987ec..c55ff5ef8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -151,7 +151,7 @@ "jest-environment-jsdom": "30.2.0", "jest-preset-angular": "16.0.0", "nx": "22.7.5", - "prettier": "3.8.3", + "prettier": "3.8.4", "prettier-plugin-organize-attributes": "1.0.0", "prisma": "7.8.0", "react": "18.2.0", @@ -33058,9 +33058,9 @@ } }, "node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", + "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index 39f2ec046..dee237dd4 100644 --- a/package.json +++ b/package.json @@ -195,7 +195,7 @@ "jest-environment-jsdom": "30.2.0", "jest-preset-angular": "16.0.0", "nx": "22.7.5", - "prettier": "3.8.3", + "prettier": "3.8.4", "prettier-plugin-organize-attributes": "1.0.0", "prisma": "7.8.0", "react": "18.2.0", From 9aabad195bdff2c11af38e42e681d5cd3586cb0a Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:30:09 +0200 Subject: [PATCH 50/60] Task/upgrade bull-board to version 8.0.1 (#7133) * Upgrade bull-board to version 8.0.1 * Update changelog --- CHANGELOG.md | 1 + package-lock.json | 40 ++++++++++++++++++++-------------------- package.json | 6 +++--- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26f396788..9fc1ed09b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Upgraded `bull-board` from version `7.2.1` to `8.0.1` - Upgraded `prettier` from version `3.8.3` to `3.8.4` ## 3.16.0 - 2026-06-24 diff --git a/package-lock.json b/package-lock.json index c55ff5ef8..40f36df1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,9 @@ "@angular/platform-browser-dynamic": "21.2.7", "@angular/router": "21.2.7", "@angular/service-worker": "21.2.7", - "@bull-board/api": "7.2.1", - "@bull-board/express": "7.2.1", - "@bull-board/nestjs": "7.2.1", + "@bull-board/api": "8.0.1", + "@bull-board/express": "8.0.1", + "@bull-board/nestjs": "8.0.1", "@codewithdan/observable-store": "2.2.15", "@date-fns/utc": "2.1.1", "@internationalized/number": "3.6.7", @@ -3543,25 +3543,25 @@ "license": "(Apache-2.0 AND BSD-3-Clause)" }, "node_modules/@bull-board/api": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-7.2.1.tgz", - "integrity": "sha512-ldRG4POJLHf6oDrbDA7AsbTKliBmV4eySlwdUAumiRDtfvtbRSdXGE4Md2uPDova1r/ck7ExEe1+pHEQAZElqw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@bull-board/api/-/api-8.0.1.tgz", + "integrity": "sha512-7FELJHRQPtjH9+r/DUArr4pDVU8r1yeDS9azQUzFtIbsUT5xGjyg5R1RB0I/RfwFfK5bqho/4cpzxJ/UnQjSKA==", "license": "MIT", "dependencies": { "redis-info": "^3.1.0" }, "peerDependencies": { - "@bull-board/ui": "7.2.1" + "@bull-board/ui": "8.0.1" } }, "node_modules/@bull-board/express": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-7.2.1.tgz", - "integrity": "sha512-tBr/xV5letzKYPRGRkilTQZmfoCoy3mCuUo4M2dDoDKOhbrF360mK5v9/rIcSgYSyI9c7BgEgrve80LhmexNxQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@bull-board/express/-/express-8.0.1.tgz", + "integrity": "sha512-VOEhLNlaaVk3mBBoREXO/Dopzr9rKK5TpOGyaCbcnpzLKlrlwhj6BAeWWreB7/3Wu+pNnbKmrAhK8OOnYdzxLg==", "license": "MIT", "dependencies": { - "@bull-board/api": "7.2.1", - "@bull-board/ui": "7.2.1", + "@bull-board/api": "8.0.1", + "@bull-board/ui": "8.0.1", "ejs": "^6.0.1", "express": "^5.2.1" } @@ -3579,12 +3579,12 @@ } }, "node_modules/@bull-board/nestjs": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@bull-board/nestjs/-/nestjs-7.2.1.tgz", - "integrity": "sha512-Uq2Z3+0ORgHJSw4TDV1kBrHdksRnK8CZdda63hrStduPnvKHPBxIZUGNfBN/vL08UqizpNkjFmNyNXiHOgf0LQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@bull-board/nestjs/-/nestjs-8.0.1.tgz", + "integrity": "sha512-yBB+S7ibdrcO6y01VQTzd51Qxx19bPnte5TSdA59BWSeH6oVagB+EkCqHXTbfdGrvLrwZEF3tnSCE6339nB7MQ==", "license": "MIT", "peerDependencies": { - "@bull-board/api": "^7.2.1", + "@bull-board/api": "^8.0.1", "@nestjs/bull-shared": "^10.0.0 || ^11.0.0", "@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0", "@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0", @@ -3593,12 +3593,12 @@ } }, "node_modules/@bull-board/ui": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-7.2.1.tgz", - "integrity": "sha512-O4ykrXrl2UJNHnhJrCvJxrw1ar+DlUBgyZUeZ8Ci+Ne5Wbq6rBv1gfpQH54/eu3IFbLso0S/kjc6WUGb2HPqZw==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@bull-board/ui/-/ui-8.0.1.tgz", + "integrity": "sha512-gKEGSD8dlUoWFGJJ4I4Q5bDtfB7yJwGfpgA2DVn1G1TzlUSN5n4fzzcGpLf5fS+QhR7tB/niydUiFRQ2zrjVrQ==", "license": "MIT", "dependencies": { - "@bull-board/api": "7.2.1" + "@bull-board/api": "8.0.1" } }, "node_modules/@cacheable/utils": { diff --git a/package.json b/package.json index dee237dd4..878e93f67 100644 --- a/package.json +++ b/package.json @@ -65,9 +65,9 @@ "@angular/platform-browser-dynamic": "21.2.7", "@angular/router": "21.2.7", "@angular/service-worker": "21.2.7", - "@bull-board/api": "7.2.1", - "@bull-board/express": "7.2.1", - "@bull-board/nestjs": "7.2.1", + "@bull-board/api": "8.0.1", + "@bull-board/express": "8.0.1", + "@bull-board/nestjs": "8.0.1", "@codewithdan/observable-store": "2.2.15", "@date-fns/utc": "2.1.1", "@internationalized/number": "3.6.7", From 13240f44d828f9ad3488192b0f7b3d481af520e7 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:31:10 +0200 Subject: [PATCH 51/60] Bugfix/improve header alignment in queue jobs table of admin control panel (#7136) * Improve alignment * Update changelog --- CHANGELOG.md | 4 ++++ apps/client/src/app/components/admin-jobs/admin-jobs.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fc1ed09b..427eec292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded `bull-board` from version `7.2.1` to `8.0.1` - Upgraded `prettier` from version `3.8.3` to `3.8.4` +### Fixed + +- Improved the table headers’ alignment in the queue jobs table of the admin control panel + ## 3.16.0 - 2026-06-24 ### Added diff --git a/apps/client/src/app/components/admin-jobs/admin-jobs.html b/apps/client/src/app/components/admin-jobs/admin-jobs.html index e615db31b..fe893690b 100644 --- a/apps/client/src/app/components/admin-jobs/admin-jobs.html +++ b/apps/client/src/app/components/admin-jobs/admin-jobs.html @@ -27,7 +27,7 @@ From 1bafb37a090d8f63cfa5eedb98ba2b2624d59338 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:31:57 +0200 Subject: [PATCH 52/60] Task/localize regions of personal finance tools (#7134) Localize regions --- .../personal-finance-tools/product-page.component.ts | 4 +++- libs/common/src/lib/personal-finance-tools.ts | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts index 6007d29fb..bb350a8db 100644 --- a/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts +++ b/apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts @@ -70,7 +70,9 @@ export class GfProductPageComponent { if (mappedProduct.regions) { mappedProduct.regions = mappedProduct.regions.map((region) => { - return translate(region); + return region === 'Global' + ? translate(region) + : getCountryName({ code: region }); }); } diff --git a/libs/common/src/lib/personal-finance-tools.ts b/libs/common/src/lib/personal-finance-tools.ts index 265d1b6b9..e5a964efc 100644 --- a/libs/common/src/lib/personal-finance-tools.ts +++ b/libs/common/src/lib/personal-finance-tools.ts @@ -780,7 +780,7 @@ export const personalFinanceTools: Product[] = [ note: 'Maybe Finance was discontinued in 2023, relaunched in 2024, and discontinued again in 2025', origin: 'US', pricingPerYear: '$145', - regions: ['United States'], + regions: ['US'], slogan: 'Your financial future, in your control', url: 'https://github.com/maybe-finance/maybe' }, @@ -792,7 +792,7 @@ export const personalFinanceTools: Product[] = [ name: 'Merlin', origin: 'US', pricingPerYear: '$204', - regions: ['Canada', 'United States'], + regions: ['CA', 'US'], slogan: 'The smartest way to track your crypto', url: 'https://www.merlincrypto.com' }, @@ -967,7 +967,7 @@ export const personalFinanceTools: Product[] = [ note: 'Originally named as Tresor One', origin: 'DE', pricingPerYear: '€99.99', - regions: ['Austria', 'Germany', 'Switzerland'], + regions: ['AT', 'CH', 'DE'], slogan: 'Dein Vermögen immer im Blick', url: 'https://www.parqet.com' }, @@ -1354,7 +1354,7 @@ export const personalFinanceTools: Product[] = [ name: 'Tresor One', note: 'Renamed to Parqet', origin: 'DE', - regions: ['Austria', 'Germany', 'Switzerland'], + regions: ['AT', 'CH', 'DE'], slogan: 'Dein Vermögen immer im Blick' }, { From 3c68ce286c7dca117dc4350e91e8715baea651c0 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:41:21 +0200 Subject: [PATCH 53/60] Task/improve country mapping for data providers (#7137) * Improve country mapping * Update changelog --- CHANGELOG.md | 1 + apps/api/src/helper/country.helper.ts | 6 +++++- .../data-enhancer/trackinsight/trackinsight.service.ts | 7 +++++-- .../financial-modeling-prep.service.ts | 6 +++--- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 427eec292..596756d18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the country mapping for data providers - Upgraded `bull-board` from version `7.2.1` to `8.0.1` - Upgraded `prettier` from version `3.8.3` to `3.8.4` diff --git a/apps/api/src/helper/country.helper.ts b/apps/api/src/helper/country.helper.ts index 9d14d8778..1d9f8f99a 100644 --- a/apps/api/src/helper/country.helper.ts +++ b/apps/api/src/helper/country.helper.ts @@ -7,8 +7,12 @@ export function getCountryCodeByName({ aliases?: Record; name: string; }): string { + if (aliases[name]) { + return aliases[name]; + } + for (const [code, country] of Object.entries(countries)) { - if (country.name === name || country.name === aliases[name]) { + if (country.name === name) { return code; } } diff --git a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts index c8291a901..9f69dc39b 100644 --- a/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts +++ b/apps/api/src/services/data-provider/data-enhancer/trackinsight/trackinsight.service.ts @@ -15,8 +15,11 @@ import { SymbolProfile } from '@prisma/client'; export class TrackinsightDataEnhancerService implements DataEnhancerInterface { private static baseUrl = 'https://www.trackinsight.com/data-api'; private static countriesMapping = { - 'Russian Federation': 'Russia', - USA: 'United States' + 'Republic of Korea': 'KR', + 'Russian Federation': 'RU', + Turkey: 'TR', + USA: 'US', + 'Virgin Islands, British': 'VG' }; private static holdingsWeightTreshold = 0.85; private static sectorsMapping: Record = { diff --git a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts index ca48bb247..ec4aa39c0 100644 --- a/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts +++ b/apps/api/src/services/data-provider/financial-modeling-prep/financial-modeling-prep.service.ts @@ -54,9 +54,9 @@ export class FinancialModelingPrepService implements DataProviderInterface, OnModuleInit { private static countriesMapping = { - 'Korea (the Republic of)': 'South Korea', - 'Russian Federation': 'Russia', - 'Taiwan (Province of China)': 'Taiwan' + 'Korea (the Republic of)': 'KR', + 'Russian Federation': 'RU', + 'Taiwan (Province of China)': 'TW' }; private readonly logger = new Logger(FinancialModelingPrepService.name); From 44d344e338016c054bafe70b485e1a52aaf35fe1 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Thu, 25 Jun 2026 17:53:19 +0200 Subject: [PATCH 54/60] Task/improve grantee display in access table (#7135) * Do not wrap grantee * Update changelog --- CHANGELOG.md | 1 + .../src/app/components/access-table/access-table.component.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 596756d18..421c68a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the grantee display in the access table to share the portfolio - Improved the country mapping for data providers - Upgraded `bull-board` from version `7.2.1` to `8.0.1` - Upgraded `prettier` from version `3.8.3` to `3.8.4` diff --git a/apps/client/src/app/components/access-table/access-table.component.html b/apps/client/src/app/components/access-table/access-table.component.html index 8ba906e0f..506190cfb 100644 --- a/apps/client/src/app/components/access-table/access-table.component.html +++ b/apps/client/src/app/components/access-table/access-table.component.html @@ -9,7 +9,7 @@ Grantee - + {{ element.grantee }} From 810aba5c205e4d68edd491b5c27f4a30c98bc7fc Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 26 Jun 2026 19:58:10 +0200 Subject: [PATCH 55/60] Task/add missing zod peer dependency (#7142) * Add zod to resolve peer dependency warnings * Update changelog --- CHANGELOG.md | 4 ++++ package-lock.json | 16 +++++++++++++--- package.json | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 421c68a11..e76751c91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- Added `zod` as a root dependency to resolve peer dependency warnings + ### Changed - Improved the grantee display in the access table to share the portfolio diff --git a/package-lock.json b/package-lock.json index 40f36df1b..23c8ca028 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,6 +96,7 @@ "twitter-api-v2": "1.29.0", "undici": "8.5.0", "yahoo-finance2": "3.15.3", + "zod": "4.4.3", "zone.js": "0.16.1" }, "devDependencies": { @@ -39945,6 +39946,15 @@ "node": ">=16" } }, + "node_modules/yahoo-finance2/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -40155,9 +40165,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 878e93f67..d74b5e787 100644 --- a/package.json +++ b/package.json @@ -140,6 +140,7 @@ "twitter-api-v2": "1.29.0", "undici": "8.5.0", "yahoo-finance2": "3.15.3", + "zod": "4.4.3", "zone.js": "0.16.1" }, "devDependencies": { From 1efb5d241c709a2650fb7cf3e7409718e697fe43 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 26 Jun 2026 20:45:52 +0200 Subject: [PATCH 56/60] Task/mask Ghostfolio data source (#7139) * Mask Ghostfolio data source --- apps/api/src/helper/data-source.helper.ts | 13 +++++++++++++ ...transform-data-source-in-response.interceptor.ts | 12 +++++++++--- .../services/data-provider/data-provider.service.ts | 12 ++++++++++-- 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 apps/api/src/helper/data-source.helper.ts diff --git a/apps/api/src/helper/data-source.helper.ts b/apps/api/src/helper/data-source.helper.ts new file mode 100644 index 000000000..c4a55c9da --- /dev/null +++ b/apps/api/src/helper/data-source.helper.ts @@ -0,0 +1,13 @@ +import { DataSource } from '@prisma/client'; + +export function getMaskedGhostfolioDataSource({ + dataSource, + ghostfolioDataSources +}: { + dataSource: DataSource; + ghostfolioDataSources: string[]; +}) { + return ghostfolioDataSources.includes(dataSource) + ? DataSource.GHOSTFOLIO + : dataSource; +} diff --git a/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts index fb8ff5dc5..ddcf30b0c 100644 --- a/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts @@ -1,3 +1,4 @@ +import { getMaskedGhostfolioDataSource } from '@ghostfolio/api/helper/data-source.helper'; import { redactPaths } from '@ghostfolio/api/helper/object.helper'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { encodeDataSource } from '@ghostfolio/common/helper'; @@ -51,10 +52,15 @@ export class TransformDataSourceInResponseInterceptor< const valueMap = this.encodedDataSourceMap; if (isExportMode) { - for (const dataSource of this.configurationService.get( + const ghostfolioDataSources = this.configurationService.get( 'DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER' - )) { - valueMap[dataSource] = 'GHOSTFOLIO'; + ) as DataSource[]; + + for (const dataSource of ghostfolioDataSources) { + valueMap[dataSource] = getMaskedGhostfolioDataSource({ + dataSource, + ghostfolioDataSources + }); } } diff --git a/apps/api/src/services/data-provider/data-provider.service.ts b/apps/api/src/services/data-provider/data-provider.service.ts index 10b0e6fd8..c6e9c83c1 100644 --- a/apps/api/src/services/data-provider/data-provider.service.ts +++ b/apps/api/src/services/data-provider/data-provider.service.ts @@ -1,5 +1,6 @@ import { ImportDataDto } from '@ghostfolio/api/app/import/import-data.dto'; import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; +import { getMaskedGhostfolioDataSource } from '@ghostfolio/api/helper/data-source.helper'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; import { MarketDataService } from '@ghostfolio/api/services/market-data/market-data.service'; @@ -221,6 +222,9 @@ export class DataProviderService implements OnModuleInit { } = {}; const dataSources = await this.getDataSources(); + const ghostfolioDataSources = this.configurationService.get( + 'DATA_SOURCES_GHOSTFOLIO_DATA_PROVIDER' + ); for (const [ index, @@ -228,6 +232,10 @@ export class DataProviderService implements OnModuleInit { ] of activitiesDto.entries()) { const activityPath = maxActivitiesToImport === 1 ? 'activity' : `activities.${index}`; + const maskedDataSource = getMaskedGhostfolioDataSource({ + dataSource, + ghostfolioDataSources + }); if (!dataSources.includes(dataSource)) { throw new Error( @@ -243,7 +251,7 @@ export class DataProviderService implements OnModuleInit { if (dataProvider.getDataProviderInfo().isPremium) { throw new Error( - `${activityPath}.dataSource ("${dataSource}") is not valid` + `${activityPath}.dataSource ("${maskedDataSource}") requires Ghostfolio Premium` ); } } @@ -306,7 +314,7 @@ export class DataProviderService implements OnModuleInit { if (!assetProfile?.name) { throw new Error( - `activities.${index}.symbol ("${symbol}") is not valid for the specified data source ("${dataSource}")` + `activities.${index}.symbol ("${symbol}") is not valid for the specified data source ("${maskedDataSource}")` ); } From f9b5c9bee2465bd46dc8563302136903676c1637 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 26 Jun 2026 21:00:54 +0200 Subject: [PATCH 57/60] Task/upgrade Nx to version 23.0.1 (#7141) * Upgrade Nx to version 23.0.1 * Update changelog --- .gitignore | 1 + CHANGELOG.md | 1 + nx.json | 3 +- package-lock.json | 6216 ++++++++------------------------------------- package.json | 24 +- 5 files changed, 1057 insertions(+), 5188 deletions(-) diff --git a/.gitignore b/.gitignore index ab31ae269..8071bf0b9 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ npm-debug.log .env.prod .github/instructions/nx.instructions.md .nx/cache +.nx/migrate-runs .nx/polygraph .nx/self-healing .nx/workspace-data diff --git a/CHANGELOG.md b/CHANGELOG.md index e76751c91..836ee7683 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved the grantee display in the access table to share the portfolio - Improved the country mapping for data providers - Upgraded `bull-board` from version `7.2.1` to `8.0.1` +- Upgraded `Nx` from version `22.7.5` to `23.0.1` - Upgraded `prettier` from version `3.8.3` to `3.8.4` ### Fixed diff --git a/nx.json b/nx.json index 1f45f532a..6b90f437c 100644 --- a/nx.json +++ b/nx.json @@ -126,5 +126,6 @@ }, "parallel": 1, "defaultBase": "origin/main", - "analytics": false + "analytics": false, + "neverConnectToCloud": true } diff --git a/package-lock.json b/package-lock.json index 23c8ca028..dbd9ca885 100644 --- a/package-lock.json +++ b/package-lock.json @@ -115,16 +115,16 @@ "@eslint/js": "9.35.0", "@nestjs/schematics": "11.1.0", "@nestjs/testing": "11.1.21", - "@nx/angular": "22.7.5", - "@nx/eslint-plugin": "22.7.5", - "@nx/jest": "22.7.5", - "@nx/js": "22.7.5", - "@nx/module-federation": "22.7.5", - "@nx/nest": "22.7.5", - "@nx/node": "22.7.5", - "@nx/storybook": "22.7.5", - "@nx/web": "22.7.5", - "@nx/workspace": "22.7.5", + "@nx/angular": "23.0.1", + "@nx/eslint-plugin": "23.0.1", + "@nx/jest": "23.0.1", + "@nx/js": "23.0.1", + "@nx/module-federation": "23.0.1", + "@nx/nest": "23.0.1", + "@nx/node": "23.0.1", + "@nx/storybook": "23.0.1", + "@nx/web": "23.0.1", + "@nx/workspace": "23.0.1", "@schematics/angular": "21.2.6", "@storybook/addon-docs": "10.1.10", "@storybook/addon-themes": "10.1.10", @@ -148,10 +148,10 @@ "eslint-plugin-import": "2.32.0", "eslint-plugin-storybook": "10.1.10", "husky": "9.1.7", - "jest": "30.2.0", + "jest": "30.3.0", "jest-environment-jsdom": "30.2.0", "jest-preset-angular": "16.0.0", - "nx": "22.7.5", + "nx": "23.0.1", "prettier": "3.8.4", "prettier-plugin-organize-attributes": "1.0.0", "prisma": "7.8.0", @@ -3670,9 +3670,9 @@ } }, "node_modules/@colordx/core": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.4.3.tgz", - "integrity": "sha512-kIxYSfA5T8HXjav55UaaH/o/cKivF6jCCGIb8eqtcsfI46wsvlSiT8jMDyrl779qLec3c2c2oHBZo4oAhvbjrQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@colordx/core/-/core-5.5.0.tgz", + "integrity": "sha512-3PxTH8itZzltK0U9jTwVVnjLXvnDYuq3m+QXsHkENxWiPRh4WaoLcs1SQjqgZ55kS+QyirpH5BVwzP2gMVG6EQ==", "dev": true, "license": "MIT" }, @@ -5267,39 +5267,38 @@ } }, "node_modules/@jest/core": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz", - "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.3.0.tgz", + "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", + "@jest/console": "30.3.0", "@jest/pattern": "30.0.1", - "@jest/reporters": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", + "@jest/reporters": "30.3.0", + "@jest/test-result": "30.3.0", + "@jest/transform": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", "ci-info": "^4.2.0", "exit-x": "^0.2.2", "graceful-fs": "^4.2.11", - "jest-changed-files": "30.2.0", - "jest-config": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", + "jest-changed-files": "30.3.0", + "jest-config": "30.3.0", + "jest-haste-map": "30.3.0", + "jest-message-util": "30.3.0", "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-resolve-dependencies": "30.2.0", - "jest-runner": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "jest-watcher": "30.2.0", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", + "jest-resolve": "30.3.0", + "jest-resolve-dependencies": "30.3.0", + "jest-runner": "30.3.0", + "jest-runtime": "30.3.0", + "jest-snapshot": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "jest-watcher": "30.3.0", + "pretty-format": "30.3.0", "slash": "^3.0.0" }, "engines": { @@ -5314,615 +5313,6 @@ } } }, - "node_modules/@jest/core/node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/reporters": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz", - "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/test-sequencer": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", - "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@jest/core/node_modules/babel-jest": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", - "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "30.2.0", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.1", - "babel-preset-jest": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-0" - } - }, - "node_modules/@jest/core/node_modules/babel-plugin-jest-hoist": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", - "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/babel__core": "^7.20.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/babel-preset-jest": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", - "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "30.2.0", - "babel-preset-current-node-syntax": "^1.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-beta.1" - } - }, - "node_modules/@jest/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/core/node_modules/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-circus": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", - "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.2.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "p-limit": "^3.1.0", - "pretty-format": "30.2.0", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-config": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", - "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.1.0", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.2.0", - "@jest/types": "30.2.0", - "babel-jest": "30.2.0", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.2.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-runner": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-each": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", - "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "jest-util": "30.2.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-environment-node": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", - "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-haste-map": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", - "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" - } - }, - "node_modules/@jest/core/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-resolve": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", - "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/diff-sequences": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", @@ -6149,55 +5539,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/expect/node_modules/@jest/snapshot-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/jest-snapshot": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", - "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.3.0", - "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "babel-preset-current-node-syntax": "^1.2.0", - "chalk": "^4.1.2", - "expect": "30.3.0", - "graceful-fs": "^4.2.11", - "jest-diff": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-util": "30.3.0", - "pretty-format": "30.3.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@jest/fake-timers": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.3.0.tgz", @@ -6227,253 +5568,21 @@ } }, "node_modules/@jest/globals": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz", - "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/types": "30.2.0", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@jest/globals/node_modules/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", + "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/environment": "30.3.0", + "@jest/expect": "30.3.0", + "@jest/types": "30.3.0", + "jest-mock": "30.3.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/globals/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/pattern": { "version": "30.0.1", "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", @@ -6545,13 +5654,13 @@ } }, "node_modules/@jest/snapshot-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz", - "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", + "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", + "@jest/types": "30.3.0", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", "natural-compare": "^1.4.0" @@ -6560,25 +5669,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/snapshot-utils/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@jest/source-map": { "version": "30.0.1", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", @@ -7400,331 +6490,63 @@ } } }, - "node_modules/@module-federation/bridge-react-webpack-plugin": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-2.5.0.tgz", - "integrity": "sha512-Ux9XVW//K6K+KHKPdc0Jnc7RtTpZaEXgbVhp5yovtFkCJVt8hEClcTeuI18MvvLiV/q2hUpCU5Wsf9zNaIYStQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/sdk": "2.5.0", - "@types/semver": "7.5.8", - "semver": "7.6.3" - } - }, - "node_modules/@module-federation/bridge-react-webpack-plugin/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@module-federation/cli": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/cli/-/cli-2.5.0.tgz", - "integrity": "sha512-+czXA6yoiiF9W6+YEOCpQE6zpGZpA89X0oCEz3EaWPTkL4chEbxurjpME8CMnJk9iuFxl167+cBQiQlVBiHGGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/dts-plugin": "2.5.0", - "@module-federation/sdk": "2.5.0", - "commander": "11.1.0", - "jiti": "2.4.2" - }, - "bin": { - "mf": "bin/mf.js" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@module-federation/dts-plugin": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-2.5.0.tgz", - "integrity": "sha512-q7KDhJ5tn2HrUV7uMuh/L3TaaztUosE+4LAb90sxx0pPPqWRwlpBpxu1REubv5BWXmU1K/Ozn14u6jRbjLVaGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/error-codes": "2.5.0", - "@module-federation/managers": "2.5.0", - "@module-federation/sdk": "2.5.0", - "@module-federation/third-party-dts-extractor": "2.5.0", - "adm-zip": "0.5.10", - "ansi-colors": "4.1.3", - "isomorphic-ws": "5.0.0", - "node-schedule": "2.1.1", - "undici": "7.24.7", - "ws": "8.18.0" - }, - "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": ">=1.0.24" - }, - "peerDependenciesMeta": { - "vue-tsc": { - "optional": true - } - } - }, - "node_modules/@module-federation/dts-plugin/node_modules/undici": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz", - "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, - "node_modules/@module-federation/enhanced": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-2.5.0.tgz", - "integrity": "sha512-P91tzwyKSCQ6AwirqvAvTqWqmTY79ndpH0uenejFw+bbLpWrjuY0q+iZUXCV/7CSNmqwH2bkA/ssuyZljmcMVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "2.5.0", - "@module-federation/cli": "2.5.0", - "@module-federation/dts-plugin": "2.5.0", - "@module-federation/error-codes": "2.5.0", - "@module-federation/inject-external-runtime-core-plugin": "2.5.0", - "@module-federation/managers": "2.5.0", - "@module-federation/manifest": "2.5.0", - "@module-federation/rspack": "2.5.0", - "@module-federation/runtime-tools": "2.5.0", - "@module-federation/sdk": "2.5.0", - "@module-federation/webpack-bundler-runtime": "2.5.0", - "schema-utils": "4.3.0", - "tapable": "2.3.0", - "upath": "2.0.1" - }, - "bin": { - "mf": "bin/mf.js" - }, - "peerDependencies": { - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": ">=1.0.24", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "vue-tsc": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/@module-federation/enhanced/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@module-federation/enhanced/node_modules/schema-utils": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", - "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/@module-federation/error-codes": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-2.5.0.tgz", - "integrity": "sha512-sq05/8Gp3csy1nr2/f76K3vLy0/xRqVtP71ibGy8BiLg7h1UxWN7G4EwAKSrPZ4FnsERGeFlIszg5Z+MqlwhFg==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.21.6.tgz", + "integrity": "sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==", "dev": true, "license": "MIT" }, - "node_modules/@module-federation/inject-external-runtime-core-plugin": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/inject-external-runtime-core-plugin/-/inject-external-runtime-core-plugin-2.5.0.tgz", - "integrity": "sha512-e2KyTHpesBrPXGHMh4d4+s2xBiNoxbiFJkPRYHMCl81a/Gu+byrMkriZcV4VM/TFvBIlrgOJisVc1nnBI5UDRQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@module-federation/runtime-tools": "2.5.0" - } - }, - "node_modules/@module-federation/managers": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-2.5.0.tgz", - "integrity": "sha512-9b5mU/7OYbKrYUJmhZ1kkfeJCZqR7qX6/FWp+oOfZMzUynN7Rb41dwoUs3TdnOKzbZ3CCwtZ2WsR4pF9ZNvuJA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/sdk": "2.5.0", - "find-pkg": "2.0.0" - } - }, - "node_modules/@module-federation/manifest": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-2.5.0.tgz", - "integrity": "sha512-pmwQCGWjM2oKY7CkR7nEDOfMK0bNFJUifuDxuOB5iOWhU+Rp92UyyBI9IbJAtiISTSFGtuKRy40peJGvQq2VcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/dts-plugin": "2.5.0", - "@module-federation/managers": "2.5.0", - "@module-federation/sdk": "2.5.0", - "find-pkg": "2.0.0" - } - }, - "node_modules/@module-federation/node": { - "version": "2.7.43", - "resolved": "https://registry.npmjs.org/@module-federation/node/-/node-2.7.43.tgz", - "integrity": "sha512-oKoLm7dqb5EvkiNIfsEdLmmBX7XLWHtPSx3M9kEYuXAaNAppoRWC9WtgrrZYXWErB2BG9wxMlx/8Xq3awRUCdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/enhanced": "2.5.0", - "@module-federation/runtime": "2.5.0", - "@module-federation/sdk": "2.5.0", - "encoding": "0.1.13", - "node-fetch": "2.7.0", - "tapable": "2.3.0" - }, - "peerDependencies": { - "webpack": "^5.40.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - } - } - }, - "node_modules/@module-federation/rspack": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-2.5.0.tgz", - "integrity": "sha512-OAFMpMXuLEQFmWBuC1I7LNDQ8N3CDANXe0YGPWkIPNxKq5Tj/KNfDidmutoYgvXlZKOM4yKBKBsL6Xt/UvtOIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/bridge-react-webpack-plugin": "2.5.0", - "@module-federation/dts-plugin": "2.5.0", - "@module-federation/inject-external-runtime-core-plugin": "2.5.0", - "@module-federation/managers": "2.5.0", - "@module-federation/manifest": "2.5.0", - "@module-federation/runtime-tools": "2.5.0", - "@module-federation/sdk": "2.5.0" - }, - "peerDependencies": { - "@rspack/core": "^0.7.0 || ^1.0.0 || ^2.0.0-0", - "typescript": "^4.9.0 || ^5.0.0", - "vue-tsc": ">=1.0.24" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - }, - "vue-tsc": { - "optional": true - } - } - }, "node_modules/@module-federation/runtime": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-2.5.0.tgz", - "integrity": "sha512-dOc7pFEf8aruHBk5hoJLnvwkCa5ELT78q3o9dqcdaa/TT74X5z0FT0BsaGaRBPcse/iP6czK3fWd7RLv5ZKP5g==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.21.6.tgz", + "integrity": "sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.5.0", - "@module-federation/runtime-core": "2.5.0", - "@module-federation/sdk": "2.5.0" + "@module-federation/error-codes": "0.21.6", + "@module-federation/runtime-core": "0.21.6", + "@module-federation/sdk": "0.21.6" } }, "node_modules/@module-federation/runtime-core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-2.5.0.tgz", - "integrity": "sha512-STmhQ3c6/hunba2FMP6GrHazXU/8GuN7Gk4dOkWNRpnqYIoD8Wx4MNl76j3HdCzBESC7uSMXTniksVaM1+xxyA==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.21.6.tgz", + "integrity": "sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.5.0", - "@module-federation/sdk": "2.5.0" + "@module-federation/error-codes": "0.21.6", + "@module-federation/sdk": "0.21.6" } }, "node_modules/@module-federation/runtime-tools": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-2.5.0.tgz", - "integrity": "sha512-fR3Na6V78ov3/O17Mev+1vydfmqlYWP4ZNxD/bBkmqKhCO7jMdthNTT02yDljlCyhYl6+X90UJlFhwFle6rIsw==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.21.6.tgz", + "integrity": "sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/runtime": "2.5.0", - "@module-federation/webpack-bundler-runtime": "2.5.0" + "@module-federation/runtime": "0.21.6", + "@module-federation/webpack-bundler-runtime": "0.21.6" } }, "node_modules/@module-federation/sdk": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-2.5.0.tgz", - "integrity": "sha512-ScU22XDyV77l50njjzewMpMlNN1CYo0tHS1D6iy+vNKWrHGq8DWVB0vwG8dmvx/WZ4uq+sXgUsQet17MoKsfZw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "node-fetch": "^2.7.0 || ^3.3.2" - }, - "peerDependenciesMeta": { - "node-fetch": { - "optional": true - } - } - }, - "node_modules/@module-federation/third-party-dts-extractor": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-2.5.0.tgz", - "integrity": "sha512-5di43LGk2ies86Cj8QyzYr540Ijc+nyPqYziyFotL6Pparnu+uf3b3ERfEyQfBmEcyGk1MpitQIO2J3bd9BcNw==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", + "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", "dev": true, - "license": "MIT", - "dependencies": { - "find-pkg": "2.0.0", - "resolve": "1.22.8" - } + "license": "MIT" }, "node_modules/@module-federation/webpack-bundler-runtime": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-2.5.0.tgz", - "integrity": "sha512-UxVad+tNZYkBnZzqJQsZa0pB5gO5cJoCjMumOo3bhzXBJVqHsFupfeHa8Nk7WrRVbJE6zRT9ZHK0s0NDWBMyJw==", + "version": "0.21.6", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.21.6.tgz", + "integrity": "sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/error-codes": "2.5.0", - "@module-federation/runtime": "2.5.0", - "@module-federation/sdk": "2.5.0" + "@module-federation/runtime": "0.21.6", + "@module-federation/sdk": "0.21.6" } }, "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { @@ -8983,20 +7805,19 @@ } }, "node_modules/@nx/angular": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/angular/-/angular-22.7.5.tgz", - "integrity": "sha512-M+xTktTN0VBGpvFsK5u+8oMPZhD3Du2nr/b2U/EpqnfWFb2y7r7nIhQT8NYjvVlGCRyKjJi6tXNHxND6KLqr0g==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/angular/-/angular-23.0.1.tgz", + "integrity": "sha512-/SJEhwGrAOO35fQjRMwLi3RBqzSaPNViCGqiQldeTokzJqtr9h4QVQxVH+mD+zLdwRI4teoC9taekVrsjXcMoA==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", - "@nx/eslint": "22.7.5", - "@nx/js": "22.7.5", - "@nx/module-federation": "22.7.5", - "@nx/rspack": "22.7.5", - "@nx/web": "22.7.5", - "@nx/webpack": "22.7.5", - "@nx/workspace": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/eslint": "23.0.1", + "@nx/js": "23.0.1", + "@nx/module-federation": "23.0.1", + "@nx/rspack": "23.0.1", + "@nx/web": "23.0.1", + "@nx/webpack": "23.0.1", "@phenomnomnominal/tsquery": "~6.2.0", "@typescript-eslint/type-utils": "^8.0.0", "enquirer": "~2.3.6", @@ -9044,19 +7865,19 @@ } }, "node_modules/@nx/cypress": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/cypress/-/cypress-22.7.5.tgz", - "integrity": "sha512-R7vlStn1ukKL9WL/dtfESKeqC38LyDvapPdSbxlBiDISCMgJAzntLcoBM22LfR3z7xy4kDk21fDMwglO3Bj30A==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/cypress/-/cypress-23.0.1.tgz", + "integrity": "sha512-is3oXDoTd2K4C7b2qq3tzKVctBDOdKsfEHIztCwV7ZO3jd9cWl83fLQUL6WI3WVoafo8ALxARRSe1ri4GkzyFg==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", - "@nx/eslint": "22.7.5", - "@nx/js": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/eslint": "23.0.1", + "@nx/js": "23.0.1", "@phenomnomnominal/tsquery": "~6.2.0", "detect-port": "^2.1.0", "semver": "^7.6.3", - "tree-kill": "1.2.2", + "tree-kill": "^1.2.2", "tslib": "^2.3.0" }, "peerDependencies": { @@ -9069,9 +7890,9 @@ } }, "node_modules/@nx/devkit": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-22.7.5.tgz", - "integrity": "sha512-/63ziS7kdHXYTLLhwWBu9hFwoFFT8xf+PkcQjsNdPqc5JmkYkSew0cE/vp5ORgBpGLWWnFPJgmfqjbJoO2C7jA==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-23.0.1.tgz", + "integrity": "sha512-A/chuNS1RZwdbRe/Nf+w0qtPEFHLcZNPzo8Abw5mBxyXmy9yvHZpuZuqDbt/lASFU+TEb74xExL1AnKWwqpOIg==", "dev": true, "license": "MIT", "dependencies": { @@ -9084,7 +7905,7 @@ "yargs-parser": "21.1.1" }, "peerDependencies": { - "nx": ">= 21 <= 23 || ^22.0.0-0" + "nx": ">= 22 <= 24 || ^23.0.0-0" } }, "node_modules/@nx/devkit/node_modules/balanced-match": { @@ -9140,32 +7961,32 @@ } }, "node_modules/@nx/docker": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/docker/-/docker-22.7.5.tgz", - "integrity": "sha512-IuizX/ACvAjoTIued7eHFDaknSL6WVfDTMtzxiqaY+iDpdOwCVTC1ZXQZSMba/xEsh4owk4qnSRmJ2eWSWburw==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/docker/-/docker-23.0.1.tgz", + "integrity": "sha512-H2/wGZa10X2KhM+BzUkZLCI9wDG1kGqDGco1tDWZczKwk9ViOcwV23ljjt5RtIQIson6B7L56pO2F/8WKJ+1CQ==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", + "@nx/devkit": "23.0.1", "enquirer": "~2.3.6", "tslib": "^2.3.0" } }, "node_modules/@nx/eslint": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-22.7.5.tgz", - "integrity": "sha512-D/85AvnF07ng/fLcSvAE8bxFQKvejUc/MP4pX6aFZgRGrpduo7mTwFMlM/UtOtTTPRRRQVLmM9u7jn4JKROBRw==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-23.0.1.tgz", + "integrity": "sha512-/P+iXDUsXHeqU7NMDviE/tjJkaVWssc7TLcCoJ6TRy/p+U7HaigLx57VAogBIznldHiyL7KbM7Y0fzqo/hPofw==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", - "@nx/js": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/js": "23.0.1", "semver": "^7.6.3", "tslib": "^2.3.0", "typescript": "~5.9.2" }, "peerDependencies": { - "@nx/jest": "22.7.5", + "@nx/jest": "23.0.1", "@zkochan/js-yaml": "0.0.7", "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0" }, @@ -9179,14 +8000,14 @@ } }, "node_modules/@nx/eslint-plugin": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/eslint-plugin/-/eslint-plugin-22.7.5.tgz", - "integrity": "sha512-C9mLUAZjcAKvkAifLNxNBWzvX9RFc/fg+GbO0d50596Lw3Yoz5tRCm4mgpUbVI3mkMIQumjoe8hu9bFx85bXnw==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/eslint-plugin/-/eslint-plugin-23.0.1.tgz", + "integrity": "sha512-fZ4fU4fYxvmHYUtiPuh9vg6KItjqaz4c8SHeqOCRvDsbm7NwQidWsGiruk853No5KnmCHfj/En87v/qGOguoGw==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", - "@nx/js": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/js": "23.0.1", "@phenomnomnominal/tsquery": "~6.2.0", "@typescript-eslint/type-utils": "^8.0.0", "@typescript-eslint/utils": "^8.0.0", @@ -9198,10 +8019,13 @@ "tslib": "^2.3.0" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.13.2 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^8.0.0", "eslint-config-prettier": "^10.0.0" }, "peerDependenciesMeta": { + "@typescript-eslint/parser": { + "optional": true + }, "eslint-config-prettier": { "optional": true } @@ -9221,16 +8045,16 @@ } }, "node_modules/@nx/jest": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-22.7.5.tgz", - "integrity": "sha512-+WlVdtDlVM1dyJKQg/gKiMMs6B4cR8Qh3NT9J2WFPbKdD89DTR771j+WZE572MLijJmqzOE7uNH189kV1qEj0A==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-23.0.1.tgz", + "integrity": "sha512-F5lhjttIExH8hEJ09cqv3Ac1o8GyGTHpD1OLLWnNAYaN/CPpbI16Ix8VLY/fuwJYf6wcm1/vBL3knbv6LABODA==", "dev": true, "license": "MIT", "dependencies": { "@jest/reporters": "^30.0.2", "@jest/test-result": "^30.0.2", - "@nx/devkit": "22.7.5", - "@nx/js": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/js": "23.0.1", "@phenomnomnominal/tsquery": "~6.2.0", "identity-obj-proxy": "3.0.0", "jest-config": "^30.0.2", @@ -9242,6 +8066,18 @@ "semver": "^7.6.3", "tslib": "^2.3.0", "yargs-parser": "21.1.1" + }, + "peerDependencies": { + "jest": "^29.0.0 || ^30.0.0", + "ts-jest": "^29.0.0" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "ts-jest": { + "optional": true + } } }, "node_modules/@nx/jest/node_modules/balanced-match": { @@ -9284,9 +8120,9 @@ } }, "node_modules/@nx/js": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/js/-/js-22.7.5.tgz", - "integrity": "sha512-2nJdlNPwYRldsdmUz+p/O8kF7eVjINaycTO4o1FXn8DL09wLvhxb1kFAaJrGA3Ig6znAnmRVGitccFt1QTPCIg==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/js/-/js-23.0.1.tgz", + "integrity": "sha512-H8jw1gk7hA8PCXBFC9ocTBpzuXOTvVQ1gA+OlEBMyKqmUaOLNm7yuoOYozwvLsLlCVY27onohSIS8xIdAR/Zow==", "dev": true, "license": "MIT", "dependencies": { @@ -9297,8 +8133,8 @@ "@babel/preset-env": "^7.23.2", "@babel/preset-typescript": "^7.22.5", "@babel/runtime": "^7.22.6", - "@nx/devkit": "22.7.5", - "@nx/workspace": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/workspace": "23.0.1", "@zkochan/js-yaml": "0.0.7", "babel-plugin-const-enum": "^1.0.1", "babel-plugin-macros": "^3.1.0", @@ -9308,7 +8144,7 @@ "detect-port": "^2.1.0", "ignore": "^7.0.5", "js-tokens": "^4.0.0", - "jsonc-parser": "3.2.0", + "jsonc-parser": "^3.2.0", "npm-run-path": "^4.0.1", "picocolors": "^1.1.0", "picomatch": "4.0.4", @@ -9318,9 +8154,13 @@ "tslib": "^2.3.0" }, "peerDependencies": { + "@swc/cli": ">=0.6.0 <0.9.0", "verdaccio": "^6.0.5" }, "peerDependenciesMeta": { + "@swc/cli": { + "optional": true + }, "verdaccio": { "optional": true } @@ -9336,13 +8176,6 @@ "node": ">= 4" } }, - "node_modules/@nx/js/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true, - "license": "MIT" - }, "node_modules/@nx/js/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9365,99 +8198,35 @@ } }, "node_modules/@nx/module-federation": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/module-federation/-/module-federation-22.7.5.tgz", - "integrity": "sha512-tb2j891NYZvl2jMFWbTgF2aLGOGDjhJrMbUKIml2/xIT6DNF89+ly8+wjMS5Nh7JAK3XSQtlERlFVBCcTD+bQA==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/module-federation/-/module-federation-23.0.1.tgz", + "integrity": "sha512-scYruTqvCegeTwAHiBL9MNRKb9RaU+XJ3+wxcmZekyNnvcQjmEPPHndE013wHUuwohyEpVFYrVCulbVb+h2Tvg==", "dev": true, "license": "MIT", "dependencies": { - "@module-federation/enhanced": "^2.3.3", - "@module-federation/node": "^2.7.21", - "@module-federation/sdk": "^2.1.0", - "@nx/devkit": "22.7.5", - "@nx/js": "22.7.5", - "@nx/web": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/js": "23.0.1", + "@nx/web": "23.0.1", "@rspack/core": "1.6.8", "express": "^4.21.2", "http-proxy-middleware": "^3.0.5", "picocolors": "^1.1.0", "tslib": "^2.3.0", - "webpack": "^5.101.3" - } - }, - "node_modules/@nx/module-federation/node_modules/@module-federation/error-codes": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.21.6.tgz", - "integrity": "sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nx/module-federation/node_modules/@module-federation/runtime": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.21.6.tgz", - "integrity": "sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/error-codes": "0.21.6", - "@module-federation/runtime-core": "0.21.6", - "@module-federation/sdk": "0.21.6" - } - }, - "node_modules/@nx/module-federation/node_modules/@module-federation/runtime-core": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.21.6.tgz", - "integrity": "sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/error-codes": "0.21.6", - "@module-federation/sdk": "0.21.6" - } - }, - "node_modules/@nx/module-federation/node_modules/@module-federation/runtime-core/node_modules/@module-federation/sdk": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", - "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nx/module-federation/node_modules/@module-federation/runtime-tools": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.21.6.tgz", - "integrity": "sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/runtime": "0.21.6", - "@module-federation/webpack-bundler-runtime": "0.21.6" - } - }, - "node_modules/@nx/module-federation/node_modules/@module-federation/runtime/node_modules/@module-federation/sdk": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", - "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nx/module-federation/node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.21.6.tgz", - "integrity": "sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/runtime": "0.21.6", - "@module-federation/sdk": "0.21.6" + "webpack": "5.105.2" + }, + "peerDependencies": { + "@module-federation/enhanced": "^2.0.0", + "@module-federation/node": "^2.0.0" + }, + "peerDependenciesMeta": { + "@module-federation/enhanced": { + "optional": true + }, + "@module-federation/node": { + "optional": true + } } }, - "node_modules/@nx/module-federation/node_modules/@module-federation/webpack-bundler-runtime/node_modules/@module-federation/sdk": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", - "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", - "dev": true, - "license": "MIT" - }, "node_modules/@nx/module-federation/node_modules/@rspack/binding": { "version": "1.6.8", "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.6.8.tgz", @@ -9797,41 +8566,79 @@ } }, "node_modules/@nx/nest": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nest/-/nest-22.7.5.tgz", - "integrity": "sha512-UhCLlH3UjankgIFcQi6ZYEgAKSfKQlk6g9jJJpN+yyPDvoQYDPOr0iPicO+dUJvX+xDOxI/jS8easNuZ64fVPA==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nest/-/nest-23.0.1.tgz", + "integrity": "sha512-/iSE/XzbOl1C1zDuQgeoKBmQAIJYwAj1PJNrnJKf2fS8Mf1s2wGdWnegNsp7iNmAWvZw3zXxhozFiNzJv/HUzA==", "dev": true, "license": "MIT", "dependencies": { "@nestjs/schematics": "^11.0.0", - "@nx/devkit": "22.7.5", - "@nx/eslint": "22.7.5", - "@nx/js": "22.7.5", - "@nx/node": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/eslint": "23.0.1", + "@nx/js": "23.0.1", + "@nx/node": "23.0.1", + "semver": "^7.6.3", "tslib": "^2.3.0" + }, + "peerDependencies": { + "@nestjs/common": ">=10.0.0 <12.0.0", + "@nestjs/core": ">=10.0.0 <12.0.0", + "reflect-metadata": ">=0.1.0 <0.3.0", + "rxjs": "^7.0.0" + }, + "peerDependenciesMeta": { + "@nestjs/common": { + "optional": true + }, + "@nestjs/core": { + "optional": true + }, + "reflect-metadata": { + "optional": true + }, + "rxjs": { + "optional": true + } } }, "node_modules/@nx/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/node/-/node-22.7.5.tgz", - "integrity": "sha512-BJckjbyOgClqD6h2mNDhjftSRsvxbuayw0vKpT9sbd1ivAVhUCqNpe/iVP/VEdTsaK3s26hacfifD8EH3fPNWQ==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/node/-/node-23.0.1.tgz", + "integrity": "sha512-s13ja7MITncR4M/uTGM786Jqco8lJs/9Kvo7jrKFg0Ct8d5XHbu+JFpulmCGPatsBhFA0E/JHSSfR+D9E7MWxA==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", - "@nx/docker": "22.7.5", - "@nx/eslint": "22.7.5", - "@nx/jest": "22.7.5", - "@nx/js": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/docker": "23.0.1", + "@nx/eslint": "23.0.1", + "@nx/jest": "23.0.1", + "@nx/js": "23.0.1", "kill-port": "^1.6.1", + "semver": "^7.6.3", "tcp-port-used": "^1.0.2", "tslib": "^2.3.0" + }, + "peerDependencies": { + "express": ">=4.0.0 <6.0.0", + "fastify": ">=4.0.0 <6.0.0", + "koa": ">=2.0.0 <4.0.0" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + }, + "fastify": { + "optional": true + }, + "koa": { + "optional": true + } } }, "node_modules/@nx/nx-darwin-arm64": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-22.7.5.tgz", - "integrity": "sha512-eoPtwx0qZqvRUD+VVOHm150AlSYwYoPxkDHBBGqKCn5nzPspb0lLWw8q83crM/L1M928YgK0WmGf3C++7eqsTA==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-23.0.1.tgz", + "integrity": "sha512-gQJvgPnbI91DBe23Th2CqD9R/S54cPS3C1f0DhyQ8YEf9rR7EEc+sVGjhgVxlhfOk2W7I1Gy6EkXwpN4aDoW4w==", "cpu": [ "arm64" ], @@ -9843,9 +8650,9 @@ ] }, "node_modules/@nx/nx-darwin-x64": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-22.7.5.tgz", - "integrity": "sha512-VLOn/ZoEn3HfjSj+yIHLCM56/el79r+9I28CkZNHaSXJQWZ3edSkcgcfYjVxCurpN2VEwDQHLBeFCH8M+lQ7wQ==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-23.0.1.tgz", + "integrity": "sha512-e/lvzHKN6gpuD7MqEtfH1fOfnR75E55ytYNt8jaRxKI6EvpCq+Q3MunDuh9GQYAkqDrUqE7AhHrHc+eKATVEHw==", "cpu": [ "x64" ], @@ -9857,9 +8664,9 @@ ] }, "node_modules/@nx/nx-freebsd-x64": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-22.7.5.tgz", - "integrity": "sha512-LEVer/E2xfGvK9Go+imMQoEninOoq/38Z2bhV1SD3AThXrp1xaLFVkW5jQ6juebeVkAeztEoMLFlr576egS0vw==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-23.0.1.tgz", + "integrity": "sha512-f582OhSYN9qHpA9Ox9qnr3kZSZ7gQHs7crmBUutmbXmZQB2TDS/TlhvYSNnxudpwHR/tuWGi2IOQqa7zGOZj1Q==", "cpu": [ "x64" ], @@ -9871,9 +8678,9 @@ ] }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-22.7.5.tgz", - "integrity": "sha512-NP27EFGpmFJM6RL1Ey/AFJ7gA2xuqtIHaw6jjSNGvfrnZRUNaway30GrVaGGeODf0DsvAty/unqoBMPy6kDHbw==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-23.0.1.tgz", + "integrity": "sha512-VjhqPc6E7aiI0e+lowrkVbdyulsmP9fgMdcX1mCzXCEu/XZDcUbZ5qveR964cMhvm5qKn0ILJtJOUqZgmOT3Xg==", "cpu": [ "arm" ], @@ -9885,9 +8692,9 @@ ] }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-22.7.5.tgz", - "integrity": "sha512-QLnkJl3HkHsPfpLiNiAiMfpfAeFpic0U1diAxF8RqChOkCpQ7ulvyBVgE1UrQxvhd+gFQ3ed5RNDxtCRw8nTiw==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-23.0.1.tgz", + "integrity": "sha512-zX2JdHQejZWB3DRgNsh77qOVYaSSjSLuBP2qIqc7EWVlCUnR7Aj3e65PTIps4LxMMmUp4twZA2ezS0rtyK2A4w==", "cpu": [ "arm64" ], @@ -9899,9 +8706,9 @@ ] }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-22.7.5.tgz", - "integrity": "sha512-cEP6KmwBgnb38+jTTaibWCjwXcHmigqhTfy0tN1be7WZr6bHxbqNLsXqKRN70PSNA3HouZcxw1cdRL8tqbPBBA==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-23.0.1.tgz", + "integrity": "sha512-9lyhxRNBgNYwHt6paq0OLzoKNoEGF5LnNW2YYrgFY8Cjtsg/Q4pcfZ1vB5o9FX9OmUgUQs3t2d4tU8YDukRUWg==", "cpu": [ "arm64" ], @@ -9913,9 +8720,9 @@ ] }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-22.7.5.tgz", - "integrity": "sha512-tbaX1tZCSpGifDNBfDdEZAMxVF3Yg4bhFP/bm1needc0diqb+Zflc0u5tM5/6BWDMITQDwenJVsNiQ8ZdtJURA==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-23.0.1.tgz", + "integrity": "sha512-kVszY2xRyyrCXgdCdM1qG1WUhDjNPZxtdWq86a0TyIRJjfJTP9NHqpyhmvj9c2RdZxKVWHotx6fBJzY6Vn2ZrA==", "cpu": [ "x64" ], @@ -9927,9 +8734,9 @@ ] }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-22.7.5.tgz", - "integrity": "sha512-H0M7csOZIgPT822LqjxSXzf4MXRND15vIkAQe3F3Jlr3Si8LC3tzbL52aVcRfgb8MF/xOB5U47mSwxWt1M2bPQ==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-23.0.1.tgz", + "integrity": "sha512-co71K2n4zcS1SYR8EBRlCvIko7M1YycO2tZL0nrCrga87AF5dzCwx+wEclpyCR/4tNOY3FrACk4gIkVskh3CdA==", "cpu": [ "x64" ], @@ -9941,9 +8748,9 @@ ] }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-22.7.5.tgz", - "integrity": "sha512-JTcZch9YAnDL1gbhqePz3DZ4x7iYemLn1yJzrjbbXAmXju2eiiJiZvJJHbV06+SP9HKXDT8RjTKuAWTdVxnHug==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-23.0.1.tgz", + "integrity": "sha512-7oma7iy5fbnn+x5AP7SFGMuleAA2R5RZm26dn+faikyQ4PXjoRAikWJJNiOWAeCA0BaMAeVedI6fJeAsVeDUKg==", "cpu": [ "arm64" ], @@ -9955,9 +8762,9 @@ ] }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-22.7.5.tgz", - "integrity": "sha512-ngcMyHdBJ9FSz2nHdbZ7gtJlFq0O2b05sPAsVMkZ18CKzdaA1qrBDJfsMO49hPCny505eiT766+CkKdaCDl5kA==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-23.0.1.tgz", + "integrity": "sha512-TE/wvBa2cpkVXmk/AXUQAneong4JReS2hyNpAUONKG1yXU7TDKe0wvn1xQXxAbyspudT9NuCnVtpVuEkRz8S+Q==", "cpu": [ "x64" ], @@ -9969,20 +8776,17 @@ ] }, "node_modules/@nx/rspack": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/rspack/-/rspack-22.7.5.tgz", - "integrity": "sha512-E0esSN1S3e0eiHPlNbMsy6guu7APga9C1J5eO9b9IL4dB5NWXUXPsRhfPSv/8hp1gIho/rmqobItaDFgyt6KBQ==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/rspack/-/rspack-23.0.1.tgz", + "integrity": "sha512-Hbn4vUrotIy4EVaPHKQX/ijuuH9+fx3ndaEdzQT0O08iJmVmABozsJ1GmQDYO4aU4qvu3eDsJr1IOQ3jqgp7vQ==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", - "@nx/js": "22.7.5", - "@nx/module-federation": "22.7.5", - "@nx/web": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/js": "23.0.1", + "@nx/module-federation": "23.0.1", + "@nx/web": "23.0.1", "@phenomnomnominal/tsquery": "~6.2.0", - "@rspack/core": "1.6.8", - "@rspack/dev-server": "^1.1.4", - "@rspack/plugin-react-refresh": "^1.0.0", "autoprefixer": "^10.4.9", "browserslist": "^4.26.0", "css-loader": "^6.4.0", @@ -9997,118 +8801,34 @@ "postcss": "^8.4.38", "postcss-import": "~14.1.0", "postcss-loader": "^8.1.1", - "sass": "^1.85.0", - "sass-embedded": "^1.83.4", - "sass-loader": "^16.0.4", + "sass": "^1.97.2", + "sass-embedded": "^1.97.2", + "sass-loader": "^16.0.7", + "semver": "^7.6.3", "source-map-loader": "^5.0.0", "style-loader": "^3.3.0", "ts-checker-rspack-plugin": "^1.1.1", "tslib": "^2.3.0", - "webpack": "^5.101.3", + "webpack": "5.105.2", "webpack-node-externals": "^3.0.0" }, "peerDependencies": { - "@module-federation/enhanced": "^2.3.3", - "@module-federation/node": "^2.7.21" - } - }, - "node_modules/@nx/rspack/node_modules/@module-federation/error-codes": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/error-codes/-/error-codes-0.21.6.tgz", - "integrity": "sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nx/rspack/node_modules/@module-federation/runtime": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.21.6.tgz", - "integrity": "sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/error-codes": "0.21.6", - "@module-federation/runtime-core": "0.21.6", - "@module-federation/sdk": "0.21.6" - } - }, - "node_modules/@nx/rspack/node_modules/@module-federation/runtime-core": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-core/-/runtime-core-0.21.6.tgz", - "integrity": "sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/error-codes": "0.21.6", - "@module-federation/sdk": "0.21.6" - } - }, - "node_modules/@nx/rspack/node_modules/@module-federation/runtime-tools": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.21.6.tgz", - "integrity": "sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/runtime": "0.21.6", - "@module-federation/webpack-bundler-runtime": "0.21.6" - } - }, - "node_modules/@nx/rspack/node_modules/@module-federation/sdk": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.21.6.tgz", - "integrity": "sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nx/rspack/node_modules/@module-federation/webpack-bundler-runtime": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.21.6.tgz", - "integrity": "sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/runtime": "0.21.6", - "@module-federation/sdk": "0.21.6" - } - }, - "node_modules/@nx/rspack/node_modules/@rspack/binding": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@rspack/binding/-/binding-1.6.8.tgz", - "integrity": "sha512-lUeL4mbwGo+nqRKqFDCm9vH2jv9FNMVt1X8jqayWRcOCPlj/2UVMEFgqjR7Pp2vlvnTKq//31KbDBJmDZq31RQ==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "@rspack/binding-darwin-arm64": "1.6.8", - "@rspack/binding-darwin-x64": "1.6.8", - "@rspack/binding-linux-arm64-gnu": "1.6.8", - "@rspack/binding-linux-arm64-musl": "1.6.8", - "@rspack/binding-linux-x64-gnu": "1.6.8", - "@rspack/binding-linux-x64-musl": "1.6.8", - "@rspack/binding-wasm32-wasi": "1.6.8", - "@rspack/binding-win32-arm64-msvc": "1.6.8", - "@rspack/binding-win32-ia32-msvc": "1.6.8", - "@rspack/binding-win32-x64-msvc": "1.6.8" - } - }, - "node_modules/@nx/rspack/node_modules/@rspack/core": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@rspack/core/-/core-1.6.8.tgz", - "integrity": "sha512-FolcIAH5FW4J2FET+qwjd1kNeFbCkd0VLuIHO0thyolEjaPSxw5qxG67DA7BZGm6PVcoiSgPLks1DL6eZ8c+fA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@module-federation/runtime-tools": "0.21.6", - "@rspack/binding": "1.6.8", - "@rspack/lite-tapable": "1.1.0" - }, - "engines": { - "node": ">=18.12.0" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.1" + "@rspack/cli": "^1.0.0", + "@rspack/core": "^1.0.0", + "@rspack/dev-server": "^1.0.0", + "@rspack/plugin-react-refresh": "^1.0.0" }, "peerDependenciesMeta": { - "@swc/helpers": { + "@rspack/cli": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "@rspack/dev-server": { + "optional": true + }, + "@rspack/plugin-react-refresh": { "optional": true } } @@ -10478,23 +9198,23 @@ } }, "node_modules/@nx/storybook": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/storybook/-/storybook-22.7.5.tgz", - "integrity": "sha512-ZA86Gdhbq93oQjbav+RyrzUzA0nEydQKLu1oY+L6LYXE+IK3Rrjr584VJW5KySAHmu4dgCNcvnS+lyZKXK5SXQ==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/storybook/-/storybook-23.0.1.tgz", + "integrity": "sha512-QNJHgxG4qwHpvpcaJ4sllpmlxHCEJjse1dTBFRGL9U+F71ye/W6vYrmHirKuV9yO1X6DR6D1rQMj6WVTGSnvzw==", "dev": true, "license": "MIT", "dependencies": { - "@nx/cypress": "22.7.5", - "@nx/devkit": "22.7.5", - "@nx/eslint": "22.7.5", - "@nx/js": "22.7.5", + "@nx/cypress": "23.0.1", + "@nx/devkit": "23.0.1", + "@nx/eslint": "23.0.1", + "@nx/js": "23.0.1", "@phenomnomnominal/tsquery": "~6.2.0", "semver": "^7.6.3", "tslib": "^2.3.0" }, "peerDependencies": { - "@nx/web": "22.7.5", - "storybook": ">=7.0.0 <11.0.0" + "@nx/web": "23.0.1", + "storybook": ">=8.0.0 <11.0.0" }, "peerDependenciesMeta": { "@nx/web": { @@ -10503,26 +9223,26 @@ } }, "node_modules/@nx/web": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/web/-/web-22.7.5.tgz", - "integrity": "sha512-mJOx3BknJhdr2T7UD4b4LuWyTa+MyXXJvYymBjHvBuCmWC5o68wuDm2y5kXrZ1WxHJYlqoLZ2281QPVyB+SZ7A==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/web/-/web-23.0.1.tgz", + "integrity": "sha512-0iwqCPJ5A27/XzqF6637dcRmJO3/TY7nQrEobS0yJ4W2xhvCIRwsWmPkuUAZgjd4622yQauHAwPQAnkIEILPiQ==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", - "@nx/js": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/js": "23.0.1", "detect-port": "^2.1.0", "http-server": "^14.1.0", "picocolors": "^1.1.0", "tslib": "^2.3.0" }, "peerDependencies": { - "@nx/cypress": "22.7.5", - "@nx/eslint": "22.7.5", - "@nx/jest": "22.7.5", - "@nx/playwright": "22.7.5", - "@nx/vite": "22.7.5", - "@nx/webpack": "22.7.5" + "@nx/cypress": "23.0.1", + "@nx/eslint": "23.0.1", + "@nx/jest": "23.0.1", + "@nx/playwright": "23.0.1", + "@nx/vite": "23.0.1", + "@nx/webpack": "23.0.1" }, "peerDependenciesMeta": { "@nx/cypress": { @@ -10546,17 +9266,17 @@ } }, "node_modules/@nx/webpack": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/webpack/-/webpack-22.7.5.tgz", - "integrity": "sha512-R6LUNAiwQANzqnRDVG2Z4nBMOUQX8Pud1BzllK+CgJkT8qK5eRjVjkBMCEm42togZ8Ax5/BYzO5w4gqUVKfvOQ==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/webpack/-/webpack-23.0.1.tgz", + "integrity": "sha512-0ELnKItRtIlcHAN8AaSY4uwabA62aAFDCvYFqadQCTeN5eBYl1VGfzenWMvGyVZoU7K9dYtPVSkZSUhltvHQgw==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.23.2", - "@nx/devkit": "22.7.5", - "@nx/js": "22.7.5", + "@nx/devkit": "23.0.1", + "@nx/js": "23.0.1", "@phenomnomnominal/tsquery": "~6.2.0", - "ajv": "^8.12.0", + "ajv": "^8.0.0", "autoprefixer": "^10.4.9", "babel-loader": "^9.1.2", "browserslist": "^4.26.0", @@ -10573,21 +9293,35 @@ "picocolors": "^1.1.0", "postcss": "^8.4.38", "postcss-import": "~14.1.0", - "postcss-loader": "^8.2.1", + "postcss-loader": "^8.1.1", "rxjs": "^7.8.0", - "sass": "^1.85.0", - "sass-embedded": "^1.83.4", - "sass-loader": "^16.0.4", + "sass": "^1.97.2", + "sass-embedded": "^1.97.2", + "sass-loader": "^16.0.7", "source-map-loader": "^5.0.0", "style-loader": "^3.3.0", "terser-webpack-plugin": "^5.3.3", "ts-loader": "^9.3.1", "tsconfig-paths-webpack-plugin": "4.2.0", "tslib": "^2.3.0", - "webpack": "^5.101.3", - "webpack-dev-server": "^5.2.1", "webpack-node-externals": "^3.0.0", "webpack-subresource-integrity": "^5.1.0" + }, + "peerDependencies": { + "webpack": "^5.0.0", + "webpack-cli": "^5.0.0 || ^6.0.0 || ^7.0.0", + "webpack-dev-server": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } } }, "node_modules/@nx/webpack/node_modules/babel-loader": { @@ -10618,33 +9352,6 @@ "node": "*" } }, - "node_modules/@nx/webpack/node_modules/cosmiconfig": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", - "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@nx/webpack/node_modules/css-loader": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", @@ -10681,16 +9388,6 @@ } } }, - "node_modules/@nx/webpack/node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, "node_modules/@nx/webpack/node_modules/loader-utils": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", @@ -10733,50 +9430,18 @@ "dev": true, "license": "MIT" }, - "node_modules/@nx/webpack/node_modules/postcss-loader": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.2.1.tgz", - "integrity": "sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==", - "dev": true, - "license": "MIT", - "dependencies": { - "cosmiconfig": "^9.0.0", - "jiti": "^2.5.1", - "semver": "^7.6.2" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || ^1.0.0 || ^2.0.0-0", - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, "node_modules/@nx/workspace": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-22.7.5.tgz", - "integrity": "sha512-f3zx8EAOl0ANd2UXZIniBoHfDvNvi2Uy65R9Rp6emdcx7rxsuTU5Eaidryleo9wIQ5cZAcMx7Wvzp5Srj8diKA==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-23.0.1.tgz", + "integrity": "sha512-VdbvMTSEzp3ONZwiy83XEu8ktykC8aEI7M4mqKs5RNKHBFg3jtao2NFo3wDqHqnn1q9Fdaj8EbyUn08BUR5L3w==", "dev": true, "license": "MIT", "dependencies": { - "@nx/devkit": "22.7.5", + "@nx/devkit": "23.0.1", "@zkochan/js-yaml": "0.0.7", "chalk": "^4.1.0", "enquirer": "~2.3.6", - "nx": "22.7.5", + "nx": "23.0.1", "picomatch": "4.0.4", "semver": "^7.6.3", "tslib": "^2.3.0", @@ -12271,6 +10936,7 @@ "integrity": "sha512-2MGdy2s2HimsDT444Bp5XnALzNRxuBNc7y0JzyuqKbHBywd4x2NeXyhWXXoxufaCFu5PBc9Qq9jyfjW2Aeh06Q==", "dev": true, "license": "MIT", + "optional": true, "peer": true, "optionalDependencies": { "@rspack/binding-darwin-arm64": "1.7.11", @@ -12608,6 +11274,7 @@ "integrity": "sha512-rsD9b+Khmot5DwCMiB3cqTQo53ioPG3M/A7BySu8+0+RS7GCxKm+Z+mtsjtG/vsu4Tn2tcqCdZtA3pgLoJB+ew==", "dev": true, "license": "MIT", + "optional": true, "peer": true, "dependencies": { "@module-federation/runtime-tools": "0.22.0", @@ -12632,6 +11299,7 @@ "integrity": "sha512-xF9SjnEy7vTdx+xekjPCV5cIHOGCkdn3pIxo9vU7gEZMIw0SvAEdsy6Uh17xaCpm8V0FWvR0SZoK9Ik6jGOaug==", "dev": true, "license": "MIT", + "optional": true, "peer": true }, "node_modules/@rspack/core/node_modules/@module-federation/runtime": { @@ -12640,6 +11308,7 @@ "integrity": "sha512-38g5iPju2tPC3KHMPxRKmy4k4onNp6ypFPS1eKGsNLUkXgHsPMBFqAjDw96iEcjri91BrahG4XcdyKi97xZzlA==", "dev": true, "license": "MIT", + "optional": true, "peer": true, "dependencies": { "@module-federation/error-codes": "0.22.0", @@ -12653,6 +11322,7 @@ "integrity": "sha512-GR1TcD6/s7zqItfhC87zAp30PqzvceoeDGYTgF3Vx2TXvsfDrhP6Qw9T4vudDQL3uJRne6t7CzdT29YyVxlgIA==", "dev": true, "license": "MIT", + "optional": true, "peer": true, "dependencies": { "@module-federation/error-codes": "0.22.0", @@ -12665,6 +11335,7 @@ "integrity": "sha512-4ScUJ/aUfEernb+4PbLdhM/c60VHl698Gn1gY21m9vyC1Ucn69fPCA1y2EwcCB7IItseRMoNhdcWQnzt/OPCNA==", "dev": true, "license": "MIT", + "optional": true, "peer": true, "dependencies": { "@module-federation/runtime": "0.22.0", @@ -12677,6 +11348,7 @@ "integrity": "sha512-x4aFNBKn2KVQRuNVC5A7SnrSCSqyfIWmm1DvubjbO9iKFe7ith5niw8dqSFBekYBg2Fwy+eMg4sEFNVvCAdo6g==", "dev": true, "license": "MIT", + "optional": true, "peer": true }, "node_modules/@rspack/core/node_modules/@module-federation/webpack-bundler-runtime": { @@ -12685,686 +11357,192 @@ "integrity": "sha512-aM8gCqXu+/4wBmJtVeMeeMN5guw3chf+2i6HajKtQv7SJfxV/f4IyNQJUeUQu9HfiAZHjqtMV5Lvq/Lvh8LdyA==", "dev": true, "license": "MIT", + "optional": true, "peer": true, "dependencies": { "@module-federation/runtime": "0.22.0", "@module-federation/sdk": "0.22.0" } }, - "node_modules/@rspack/dev-server": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@rspack/dev-server/-/dev-server-1.2.1.tgz", - "integrity": "sha512-e/ARvskYn2Qdd02qLvc0i6H9BnOmzP0xGHS2XCr7GZ3t2k5uC5ZlLkeN1iEebU0FkAW+6ot89NahFo3nupKuww==", + "node_modules/@rspack/lite-tapable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.0.tgz", + "integrity": "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.13", - "@types/connect-history-api-fallback": "^1.5.4", - "@types/express": "^4.17.25", - "@types/express-serve-static-core": "^4.17.21", - "@types/serve-index": "^1.9.4", - "@types/serve-static": "^1.15.5", - "@types/sockjs": "^0.3.36", - "@types/ws": "^8.5.10", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.2.1", - "chokidar": "^3.6.0", - "colorette": "^2.0.10", - "compression": "^1.8.1", - "connect-history-api-fallback": "^2.0.0", - "express": "^4.22.1", - "graceful-fs": "^4.2.6", - "http-proxy-middleware": "^2.0.9", - "ipaddr.js": "^2.1.0", - "launch-editor": "^2.6.1", - "open": "^10.0.3", - "p-retry": "^6.2.0", - "schema-utils": "^4.2.0", - "selfsigned": "^2.4.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^7.4.2", - "ws": "^8.18.0" - }, - "engines": { - "node": ">= 18.12.0" - }, - "peerDependencies": { - "@rspack/core": "*" - } + "license": "MIT" }, - "node_modules/@rspack/dev-server/node_modules/@types/express": { - "version": "4.17.25", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", - "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true, + "license": "MIT" + }, + "node_modules/@schematics/angular": { + "version": "21.2.6", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.2.6.tgz", + "integrity": "sha512-KpLD8R2S762jbLdNEepE+b7KjhVOKPFHHdgNqhPv0NiGLdsvXSOx1e63JvFacoCZdmP7n3/gwmyT/utcVvnsag==", "license": "MIT", "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "^1" + "@angular-devkit/core": "21.2.6", + "@angular-devkit/schematics": "21.2.6", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "node_modules/@rspack/dev-server/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "node_modules/@sigstore/bundle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", + "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": ">= 0.6" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@rspack/dev-server/node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "node_modules/@sigstore/core": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.2.0.tgz", + "integrity": "sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA==", "dev": true, - "license": "MIT", - "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.15.1", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@rspack/dev-server/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/@sigstore/protobuf-specs": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.1.tgz", + "integrity": "sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g==", "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@rspack/dev-server/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "node_modules/@sigstore/sign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.1.tgz", + "integrity": "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "safe-buffer": "5.2.1" + "@gar/promise-retry": "^1.0.2", + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.2.0", + "@sigstore/protobuf-specs": "^0.5.0", + "make-fetch-happen": "^15.0.4", + "proc-log": "^6.1.0" }, "engines": { - "node": ">= 0.6" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@rspack/dev-server/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@sigstore/tuf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.2.tgz", + "integrity": "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "ms": "2.0.0" + "@sigstore/protobuf-specs": "^0.5.0", + "tuf-js": "^4.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@rspack/dev-server/node_modules/express": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", - "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "node_modules/@sigstore/verify": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz", + "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.5", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "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.15.1", - "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" + "@sigstore/bundle": "^4.0.0", + "@sigstore/core": "^3.1.0", + "@sigstore/protobuf-specs": "^0.5.0" }, "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@rspack/dev-server/node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "dev": true, + "node_modules/@simplewebauthn/browser": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.3.0.tgz", + "integrity": "sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==", + "license": "MIT" + }, + "node_modules/@simplewebauthn/server": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.3.1.tgz", + "integrity": "sha512-GV/oM/qeycWn8p42JZIMJBsXWQcNFg+nJFzeQTnMA4gN8mXg0+HZFWJerHg8ZN/zlveMS3iV1wzuFpOVWS/46w==", "license": "MIT", "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" + "@hexagon/base64": "^1.1.27", + "@levischuck/tiny-cbor": "^0.2.2", + "@peculiar/asn1-android": "^2.6.0", + "@peculiar/asn1-ecc": "^2.6.1", + "@peculiar/asn1-rsa": "^2.6.1", + "@peculiar/asn1-schema": "^2.6.0", + "@peculiar/asn1-x509": "^2.6.1", + "@peculiar/x509": "^1.14.3" }, "engines": { - "node": ">= 0.8" + "node": ">=20.0.0" } }, - "node_modules/@rspack/dev-server/node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/@sinclair/typebox": { + "version": "0.34.49", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", + "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } + "license": "MIT" }, - "node_modules/@rspack/dev-server/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "license": "ISC", + "license": "BSD-3-Clause", "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "type-detect": "4.0.8" } }, - "node_modules/@rspack/dev-server/node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "node_modules/@sinonjs/fake-timers": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.0.tgz", + "integrity": "sha512-m2xozxSfCIxjDdvbhIWazlP2i2aha/iUmbl94alpsIbd3iLTfeXgfBVbwyWogB6l++istyGZqamgA/EcqYf+Bg==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@stencil/core": { + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.43.0.tgz", + "integrity": "sha512-6Uj2Z3lzLuufYAE7asZ6NLKgSwsB9uxl84Eh34PASnUjfj32GkrP4DtKK7fNeh1WFGGyffsTDka3gwtl+4reUg==", + "license": "MIT", + "bin": { + "stencil": "bin/stencil" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/@rspack/dev-server/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@rspack/dev-server/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@rspack/dev-server/node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@rspack/dev-server/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@rspack/dev-server/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@rspack/dev-server/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rspack/dev-server/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@rspack/dev-server/node_modules/open": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", - "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "wsl-utils": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@rspack/dev-server/node_modules/path-to-regexp": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rspack/dev-server/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@rspack/dev-server/node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@rspack/dev-server/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/@rspack/dev-server/node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "dev": true, - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@rspack/dev-server/node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rspack/dev-server/node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@rspack/dev-server/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@rspack/dev-server/node_modules/wsl-utils": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", - "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@rspack/lite-tapable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.0.tgz", - "integrity": "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rspack/plugin-react-refresh": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@rspack/plugin-react-refresh/-/plugin-react-refresh-1.6.2.tgz", - "integrity": "sha512-k+/VrfTNgo+KirjI6V+8CWRj6y+DH9jOUWv8JorYY4vKf/9xfnZ8xHzuB4iqCwTtoZl9YnxOaOuoyjJipc2tiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "error-stack-parser": "^2.1.4" - }, - "peerDependencies": { - "react-refresh": ">=0.10.0 <1.0.0", - "webpack-hot-middleware": "2.x" - }, - "peerDependenciesMeta": { - "webpack-hot-middleware": { - "optional": true - } - } - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@schematics/angular": { - "version": "21.2.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.2.6.tgz", - "integrity": "sha512-KpLD8R2S762jbLdNEepE+b7KjhVOKPFHHdgNqhPv0NiGLdsvXSOx1e63JvFacoCZdmP7n3/gwmyT/utcVvnsag==", - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "21.2.6", - "@angular-devkit/schematics": "21.2.6", - "jsonc-parser": "3.3.1" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@sigstore/bundle": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-4.0.0.tgz", - "integrity": "sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@sigstore/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-3.2.0.tgz", - "integrity": "sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@sigstore/protobuf-specs": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.5.1.tgz", - "integrity": "sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/@sigstore/sign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-4.1.1.tgz", - "integrity": "sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@gar/promise-retry": "^1.0.2", - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.2.0", - "@sigstore/protobuf-specs": "^0.5.0", - "make-fetch-happen": "^15.0.4", - "proc-log": "^6.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@sigstore/tuf": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-4.0.2.tgz", - "integrity": "sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/protobuf-specs": "^0.5.0", - "tuf-js": "^4.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@sigstore/verify": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-3.1.0.tgz", - "integrity": "sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@sigstore/bundle": "^4.0.0", - "@sigstore/core": "^3.1.0", - "@sigstore/protobuf-specs": "^0.5.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@simplewebauthn/browser": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.3.0.tgz", - "integrity": "sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==", - "license": "MIT" - }, - "node_modules/@simplewebauthn/server": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.3.1.tgz", - "integrity": "sha512-GV/oM/qeycWn8p42JZIMJBsXWQcNFg+nJFzeQTnMA4gN8mXg0+HZFWJerHg8ZN/zlveMS3iV1wzuFpOVWS/46w==", - "license": "MIT", - "dependencies": { - "@hexagon/base64": "^1.1.27", - "@levischuck/tiny-cbor": "^0.2.2", - "@peculiar/asn1-android": "^2.6.0", - "@peculiar/asn1-ecc": "^2.6.1", - "@peculiar/asn1-rsa": "^2.6.1", - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "@peculiar/x509": "^1.14.3" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.49", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", - "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.0.tgz", - "integrity": "sha512-m2xozxSfCIxjDdvbhIWazlP2i2aha/iUmbl94alpsIbd3iLTfeXgfBVbwyWogB6l++istyGZqamgA/EcqYf+Bg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@stencil/core": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.43.0.tgz", - "integrity": "sha512-6Uj2Z3lzLuufYAE7asZ6NLKgSwsB9uxl84Eh34PASnUjfj32GkrP4DtKK7fNeh1WFGGyffsTDka3gwtl+4reUg==", - "license": "MIT", - "bin": { - "stencil": "bin/stencil" - }, - "engines": { - "node": ">=16.0.0", - "npm": ">=7.10.0" + "node": ">=16.0.0", + "npm": ">=7.10.0" }, "optionalDependencies": { "@rollup/rollup-darwin-arm64": "4.34.9", @@ -14701,16 +12879,6 @@ "undici-types": "~6.21.0" } }, - "node_modules/@types/node-forge": { - "version": "1.3.14", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz", - "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/oauth": { "version": "0.9.6", "resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.6.tgz", @@ -14839,13 +13007,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", @@ -16380,16 +14541,6 @@ "node": ">=8.9.0" } }, - "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -21136,8 +19287,9 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "devOptional": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.2" } @@ -21171,8 +19323,9 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "devOptional": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -21293,16 +19446,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, "node_modules/es-abstract": { "version": "1.24.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", @@ -22096,19 +20239,6 @@ "node": ">= 0.8.0" } }, - "node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/expect": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", @@ -22691,32 +20821,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-file-up": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-2.0.1.tgz", - "integrity": "sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-dir": "^1.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-2.0.0.tgz", - "integrity": "sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-file-up": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -22982,17 +21086,17 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -23456,58 +21560,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/global-prefix/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -23789,9 +21841,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -23819,19 +21871,6 @@ "node": ">=16.0.0" } }, - "node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "parse-passwd": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/hono": { "version": "4.12.14", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz", @@ -24583,13 +22622,13 @@ } }, "node_modules/ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/ipaddr.js": { @@ -25193,16 +23232,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-wsl": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", @@ -25220,15 +23249,15 @@ } }, "node_modules/is2": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.9.tgz", - "integrity": "sha512-rZkHeBn9Zzq52sd9IUIV3a5mfwBY+o2HePMh0wkGBM4z4qjvy2GwVxQ6nNXSfw6MmVP6gf1QIlWjiOavhM3x5g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is2/-/is2-2.0.1.tgz", + "integrity": "sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA==", "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", - "ip-regex": "^4.1.0", - "is-url": "^1.2.4" + "ip-regex": "^2.1.0", + "is-url": "^1.2.2" }, "engines": { "node": ">=v0.10.0" @@ -25257,16 +23286,6 @@ "node": ">=0.10.0" } }, - "node_modules/isomorphic-ws": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", - "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ws": "*" - } - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -25387,16 +23406,16 @@ "license": "MIT" }, "node_modules/jest": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz", - "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.3.0.tgz", + "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/core": "30.2.0", - "@jest/types": "30.2.0", + "@jest/core": "30.3.0", + "@jest/types": "30.3.0", "import-local": "^3.2.0", - "jest-cli": "30.2.0" + "jest-cli": "30.3.0" }, "bin": { "jest": "bin/jest.js" @@ -25414,57 +23433,20 @@ } }, "node_modules/jest-changed-files": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz", - "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.3.0.tgz", + "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", "dev": true, "license": "MIT", "dependencies": { "execa": "^5.1.1", - "jest-util": "30.2.0", + "jest-util": "30.3.0", "p-limit": "^3.1.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/jest-circus": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.3.0.tgz", @@ -25497,217 +23479,229 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-circus/node_modules/@jest/globals": { + "node_modules/jest-cli": { "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", - "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.3.0.tgz", + "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", + "@jest/core": "30.3.0", + "@jest/test-result": "30.3.0", "@jest/types": "30.3.0", - "jest-mock": "30.3.0" + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/jest-circus/node_modules/@jest/snapshot-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", + "node_modules/jest-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-cli/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-circus/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "node_modules/jest-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/jest-circus/node_modules/jest-runtime": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", - "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", + "node_modules/jest-cli/node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/globals": "30.3.0", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.3.0", - "@jest/transform": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.5.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-snapshot": "30.3.0", - "jest-util": "30.3.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12" } }, - "node_modules/jest-circus/node_modules/jest-snapshot": { + "node_modules/jest-config": { "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", - "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", + "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.3.0", "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.3.0", - "@jest/transform": "30.3.0", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.3.0", "@jest/types": "30.3.0", - "babel-preset-current-node-syntax": "^1.2.0", + "babel-jest": "30.3.0", "chalk": "^4.1.2", - "expect": "30.3.0", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.5.0", "graceful-fs": "^4.2.11", - "jest-diff": "30.3.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", + "jest-circus": "30.3.0", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.3.0", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.3.0", + "jest-runner": "30.3.0", "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "parse-json": "^5.2.0", "pretty-format": "30.3.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", - "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "yargs": "^17.7.2" - }, - "bin": { - "jest": "bin/jest.js" + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" }, "peerDependenciesMeta": { - "node-notifier": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { "optional": true } } }, - "node_modules/jest-cli/node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", + "node_modules/jest-diff": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", + "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", + "@jest/diff-sequences": "30.3.0", + "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" + "pretty-format": "30.3.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "node_modules/jest-docblock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", + "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "node_modules/jest-each": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", + "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" + "@jest/get-type": "30.1.0", + "@jest/types": "30.3.0", + "chalk": "^4.1.2", + "jest-util": "30.3.0", + "pretty-format": "30.3.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/@jest/expect": { + "node_modules/jest-environment-jsdom": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.2.0.tgz", + "integrity": "sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" + "@jest/environment": "30.2.0", + "@jest/environment-jsdom-abstract": "30.2.0", + "@types/jsdom": "^21.1.7", + "@types/node": "*", + "jsdom": "^26.1.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/jest-cli/node_modules/@jest/expect-utils": { + "node_modules/jest-environment-jsdom/node_modules/@jest/environment": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0" + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/@jest/fake-timers": { + "node_modules/jest-environment-jsdom/node_modules/@jest/fake-timers": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", @@ -25725,66 +23719,7 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli/node_modules/@jest/test-sequencer": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", - "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli/node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { + "node_modules/jest-environment-jsdom/node_modules/@jest/types": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", @@ -25803,7 +23738,7 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/@sinonjs/fake-timers": { + "node_modules/jest-environment-jsdom/node_modules/@sinonjs/fake-timers": { "version": "13.0.5", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", @@ -25813,564 +23748,454 @@ "@sinonjs/commons": "^3.0.1" } }, - "node_modules/jest-cli/node_modules/babel-jest": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", - "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", + "node_modules/jest-environment-jsdom/node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "30.2.0", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.1", - "babel-preset-jest": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" + } + }, + "node_modules/jest-environment-jsdom/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-0" + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/jest-cli/node_modules/babel-plugin-jest-hoist": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", - "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", + "node_modules/jest-environment-jsdom/node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/babel__core": "^7.20.5" + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/jest-cli/node_modules/babel-preset-jest": { + "node_modules/jest-environment-jsdom/node_modules/jest-message-util": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz", - "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "30.2.0", - "babel-preset-current-node-syntax": "^1.2.0" + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-beta.1" } }, - "node_modules/jest-cli/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-cli/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-cli/node_modules/expect": { + "node_modules/jest-environment-jsdom/node_modules/jest-mock": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", "jest-util": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/jest-circus": { + "node_modules/jest-environment-jsdom/node_modules/jest-util": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", - "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/test-result": "30.2.0", "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.2.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "p-limit": "^3.1.0", - "pretty-format": "30.2.0", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/jest-config": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", - "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==", + "node_modules/jest-environment-jsdom/node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.1.0", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.2.0", - "@jest/types": "30.2.0", - "babel-jest": "30.2.0", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.2.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-runner": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" }, "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" + "canvas": "^3.0.0" }, "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { + "canvas": { "optional": true } } }, - "node_modules/jest-cli/node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "node_modules/jest-environment-jsdom/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" + "entities": "^6.0.0" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/jest-cli/node_modules/jest-each": { + "node_modules/jest-environment-jsdom/node_modules/pretty-format": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", - "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "jest-util": "30.2.0", - "pretty-format": "30.2.0" + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/jest-environment-node": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", - "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", + "node_modules/jest-environment-jsdom/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-cli/node_modules/jest-haste-map": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", - "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", + "node_modules/jest-environment-jsdom/node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "tldts-core": "^6.1.86" }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "bin": { + "tldts": "bin/cli.js" } }, - "node_modules/jest-cli/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "node_modules/jest-environment-jsdom/node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/jest-environment-jsdom/node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" + "tldts": "^6.1.32" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=16" } }, - "node_modules/jest-cli/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "node_modules/jest-environment-jsdom/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "punycode": "^2.3.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/jest-cli/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "node_modules/jest-environment-jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, + "license": "BSD-2-Clause", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=12" } }, - "node_modules/jest-cli/node_modules/jest-resolve": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", - "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", + "node_modules/jest-environment-jsdom/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/jest-cli/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "node_modules/jest-environment-node": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", + "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", + "@jest/environment": "30.3.0", + "@jest/fake-timers": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "jest-mock": "30.3.0", + "jest-util": "30.3.0", + "jest-validate": "30.3.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "node_modules/jest-haste-map": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", + "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", "dependencies": { + "@jest/types": "30.3.0", "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.3.0", + "jest-worker": "30.3.0", + "picomatch": "^4.0.3", + "walker": "^1.0.8" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" } }, - "node_modules/jest-cli/node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/jest-leak-detector": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", + "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@jest/get-type": "30.1.0", + "pretty-format": "30.3.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "node_modules/jest-matcher-utils": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", + "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.3.0", + "pretty-format": "30.3.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/jest-message-util": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", + "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", "dev": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.3.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.3", + "pretty-format": "30.3.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "node_modules/jest-mock": { + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", + "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", "dev": true, "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "@jest/types": "30.3.0", + "@types/node": "*", + "jest-util": "30.3.0" }, "engines": { - "node": ">=12" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-config": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.3.0.tgz", - "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.1.0", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.3.0", - "@jest/types": "30.3.0", - "babel-jest": "30.3.0", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.5.0", - "graceful-fs": "^4.2.11", - "jest-circus": "30.3.0", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.3.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.3.0", - "jest-runner": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "parse-json": "^5.2.0", - "pretty-format": "30.3.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6" }, "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" + "jest-resolve": "*" }, "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { + "jest-resolve": { "optional": true } } }, - "node_modules/jest-config/node_modules/@jest/globals": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.3.0.tgz", - "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", + "node_modules/jest-preset-angular": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-16.0.0.tgz", + "integrity": "sha512-FVo98EZiJ9cwHeteJozCCIkgJecytt1tu0t8DrAMTyyQ4x/seeZmctkWXP0J9uGyARS0Kcwd+f2YeKqKQOB2yA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.3.0", - "@jest/expect": "30.3.0", - "@jest/types": "30.3.0", - "jest-mock": "30.3.0" + "@jest/environment-jsdom-abstract": "^30.0.0", + "bs-logger": "^0.2.6", + "esbuild-wasm": ">=0.23.0", + "jest-util": "^30.0.0", + "pretty-format": "^30.0.0", + "ts-jest": "^29.4.0" }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "optionalDependencies": { + "esbuild": ">=0.23.0" + }, + "peerDependencies": { + "@angular/compiler-cli": ">=19.0.0 <22.0.0", + "@angular/core": ">=19.0.0 <22.0.0", + "@angular/platform-browser": ">=19.0.0 <22.0.0", + "@angular/platform-browser-dynamic": ">=19.0.0 <22.0.0", + "jest": "^30.0.0", + "jsdom": ">=26.0.0", + "typescript": ">=5.5" + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-config/node_modules/@jest/snapshot-utils": { + "node_modules/jest-resolve": { "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz", - "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", + "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.3.0", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" + "jest-haste-map": "30.3.0", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.3.0", + "jest-validate": "30.3.0", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-config/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-config/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-config/node_modules/jest-leak-detector": { + "node_modules/jest-resolve-dependencies": { "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz", - "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz", + "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0", - "pretty-format": "30.3.0" + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.3.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-config/node_modules/jest-runner": { + "node_modules/jest-runner": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.3.0.tgz", "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", @@ -26404,7 +24229,28 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-config/node_modules/jest-runtime": { + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.3.0.tgz", "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", @@ -26438,7 +24284,14 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-config/node_modules/jest-snapshot": { + "node_modules/jest-runtime/node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-snapshot": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.3.0.tgz", "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", @@ -26471,1732 +24324,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-config/node_modules/jest-validate": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", - "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-config/node_modules/jest-watcher": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", - "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.3.0", - "string-length": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-config/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-config/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.3.0", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz", - "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-each": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.3.0.tgz", - "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", - "chalk": "^4.1.2", - "jest-util": "30.3.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.2.0.tgz", - "integrity": "sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/environment-jsdom-abstract": "30.2.0", - "@types/jsdom": "^21.1.7", - "@types/node": "*", - "jsdom": "^26.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/jest-environment-jsdom/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jest-environment-jsdom/node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-jsdom/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^6.1.86" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/jest-environment-jsdom/node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-environment-jsdom/node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/jest-environment-jsdom/node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/jest-environment-jsdom/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-environment-jsdom/node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/jest-environment-node": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.3.0.tgz", - "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.3.0", - "@jest/fake-timers": "30.3.0", - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-mock": "30.3.0", - "jest-util": "30.3.0", - "jest-validate": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-environment-node/node_modules/jest-validate": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", - "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.3.0.tgz", - "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.3.0", - "jest-worker": "30.3.0", - "picomatch": "^4.0.3", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" - } - }, - "node_modules/jest-leak-detector": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", - "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", - "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.3.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3", - "pretty-format": "30.3.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-mock": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", - "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-preset-angular": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-16.0.0.tgz", - "integrity": "sha512-FVo98EZiJ9cwHeteJozCCIkgJecytt1tu0t8DrAMTyyQ4x/seeZmctkWXP0J9uGyARS0Kcwd+f2YeKqKQOB2yA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment-jsdom-abstract": "^30.0.0", - "bs-logger": "^0.2.6", - "esbuild-wasm": ">=0.23.0", - "jest-util": "^30.0.0", - "pretty-format": "^30.0.0", - "ts-jest": "^29.4.0" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" - }, - "optionalDependencies": { - "esbuild": ">=0.23.0" - }, - "peerDependencies": { - "@angular/compiler-cli": ">=19.0.0 <22.0.0", - "@angular/core": ">=19.0.0 <22.0.0", - "@angular/platform-browser": ">=19.0.0 <22.0.0", - "@angular/platform-browser-dynamic": ">=19.0.0 <22.0.0", - "jest": "^30.0.0", - "jsdom": ">=26.0.0", - "typescript": ">=5.5" - } - }, - "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.3.0.tgz", - "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.3.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.3.0", - "jest-validate": "30.3.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz", - "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-resolve/node_modules/jest-validate": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", - "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.3.0", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", - "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/environment": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-leak-detector": "30.2.0", - "jest-message-util": "30.2.0", - "jest-resolve": "30.2.0", - "jest-runtime": "30.2.0", - "jest-util": "30.2.0", - "jest-watcher": "30.2.0", - "jest-worker": "30.2.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/jest-runner/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-runner/node_modules/jest-environment-node": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", - "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-haste-map": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", - "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" - } - }, - "node_modules/jest-runner/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-resolve": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", - "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", - "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/globals": "30.2.0", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/jest-runtime/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-runtime/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-runtime/node_modules/jest-haste-map": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", - "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" - } - }, - "node_modules/jest-runtime/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-resolve": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz", - "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.2.0", - "jest-validate": "30.2.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz", - "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "@jest/snapshot-utils": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", - "babel-preset-current-node-syntax": "^1.2.0", - "chalk": "^4.1.2", - "expect": "30.2.0", - "graceful-fs": "^4.2.11", - "jest-diff": "30.2.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "pretty-format": "30.2.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/transform": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz", - "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.2.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.1", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.2.0", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-snapshot/node_modules/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-haste-map": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz", - "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.2.0", - "jest-worker": "30.2.0", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" - } - }, - "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-util": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", @@ -28216,37 +24343,18 @@ } }, "node_modules/jest-validate": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz", - "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.3.0.tgz", + "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "license": "MIT", "dependencies": { "@jest/get-type": "30.1.0", - "@jest/types": "30.2.0", + "@jest/types": "30.3.0", "camelcase": "^6.3.0", "chalk": "^4.1.2", "leven": "^3.1.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "pretty-format": "30.3.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -28265,174 +24373,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-validate/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-watcher": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz", - "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==", + "version": "30.3.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.3.0.tgz", + "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", + "@jest/test-result": "30.3.0", + "@jest/types": "30.3.0", "@types/node": "*", "ansi-escapes": "^4.3.2", "chalk": "^4.1.2", "emittery": "^0.13.1", - "jest-util": "30.2.0", + "jest-util": "30.3.0", "string-length": "^4.0.2" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-watcher/node_modules/@jest/console": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz", - "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.2.0", - "jest-util": "30.2.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/test-result": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz", - "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.2.0", - "@jest/types": "30.2.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-worker": { "version": "30.3.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.3.0.tgz", @@ -28466,31 +24426,13 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/jiti": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "devOptional": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -29556,13 +25498,6 @@ "devOptional": true, "license": "Apache-2.0" }, - "node_modules/long-timeout": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", - "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==", - "dev": true, - "license": "MIT" - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -30698,21 +26633,6 @@ "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", "license": "MIT" }, - "node_modules/node-schedule": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", - "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "cron-parser": "^4.2.0", - "long-timeout": "0.1.1", - "sorted-array-functions": "^1.3.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/nopt": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", @@ -30883,9 +26803,9 @@ "license": "MIT" }, "node_modules/nx": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/nx/-/nx-22.7.5.tgz", - "integrity": "sha512-zoxsJabb33jl1QYnalDn0bicryrEBgSzdKp90d7VGGv/jDgzKrcLg/hw2ZxeYiOjWPIT/o8QNT9G9vTs4dv3AQ==", + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/nx/-/nx-23.0.1.tgz", + "integrity": "sha512-HnK0Ke8FcPeVQffYm1oyzkLNn7khrI8SeDeC3iyLhw/UEMCB24hjI5JSs6Amlyeb0/GaeiuQuts8NkQKd/NpGA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -30937,7 +26857,7 @@ "figures": "3.2.0", "flat": "5.0.2", "follow-redirects": "1.16.0", - "form-data": "4.0.5", + "form-data": "4.0.6", "fs-constants": "1.0.0", "function-bind": "1.1.2", "get-caller-file": "2.0.5", @@ -30947,7 +26867,7 @@ "has-flag": "4.0.0", "has-symbols": "1.1.0", "has-tostringtag": "1.0.2", - "hasown": "2.0.2", + "hasown": "2.0.4", "ieee754": "1.2.1", "ignore": "7.0.5", "inherits": "2.0.4", @@ -30956,8 +26876,9 @@ "is-interactive": "1.0.0", "is-unicode-supported": "0.1.0", "is-wsl": "2.2.0", + "isexe": "2.0.0", "json5": "2.2.3", - "jsonc-parser": "3.2.0", + "jsonc-parser": "3.3.1", "lines-and-columns": "2.0.3", "log-symbols": "4.1.0", "math-intrinsics": "1.1.0", @@ -30970,7 +26891,7 @@ "once": "1.4.0", "onetime": "5.1.2", "open": "8.4.2", - "ora": "5.3.0", + "ora": "5.4.1", "path-key": "3.1.1", "picocolors": "1.1.1", "proxy-from-env": "2.1.0", @@ -30988,12 +26909,12 @@ "strip-bom": "3.0.0", "supports-color": "7.2.0", "tar-stream": "2.2.0", - "tmp": "0.2.6", - "tree-kill": "1.2.2", + "tmp": "0.2.7", "tsconfig-paths": "4.2.0", "tslib": "2.8.1", "util-deprecate": "1.0.2", "wcwidth": "1.0.1", + "which": "3.0.1", "wrap-ansi": "7.0.0", "wrappy": "1.0.2", "y18n": "5.0.8", @@ -31006,16 +26927,16 @@ "nx-cloud": "dist/bin/nx-cloud.js" }, "optionalDependencies": { - "@nx/nx-darwin-arm64": "22.7.5", - "@nx/nx-darwin-x64": "22.7.5", - "@nx/nx-freebsd-x64": "22.7.5", - "@nx/nx-linux-arm-gnueabihf": "22.7.5", - "@nx/nx-linux-arm64-gnu": "22.7.5", - "@nx/nx-linux-arm64-musl": "22.7.5", - "@nx/nx-linux-x64-gnu": "22.7.5", - "@nx/nx-linux-x64-musl": "22.7.5", - "@nx/nx-win32-arm64-msvc": "22.7.5", - "@nx/nx-win32-x64-msvc": "22.7.5" + "@nx/nx-darwin-arm64": "23.0.1", + "@nx/nx-darwin-x64": "23.0.1", + "@nx/nx-freebsd-x64": "23.0.1", + "@nx/nx-linux-arm-gnueabihf": "23.0.1", + "@nx/nx-linux-arm64-gnu": "23.0.1", + "@nx/nx-linux-arm64-musl": "23.0.1", + "@nx/nx-linux-x64-gnu": "23.0.1", + "@nx/nx-linux-x64-musl": "23.0.1", + "@nx/nx-win32-arm64-msvc": "23.0.1", + "@nx/nx-win32-x64-msvc": "23.0.1" }, "peerDependencies": { "@swc-node/register": "^1.11.1", @@ -31242,13 +27163,6 @@ "node": ">=8" } }, - "node_modules/nx/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true, - "license": "MIT" - }, "node_modules/nx/node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -31324,18 +27238,19 @@ } }, "node_modules/nx/node_modules/ora": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", - "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, "license": "MIT", "dependencies": { - "bl": "^4.0.3", + "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", - "log-symbols": "^4.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" }, @@ -31407,6 +27322,22 @@ "node": ">=6" } }, + "node_modules/nx/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/nx/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -32014,16 +27945,6 @@ "node": ">= 0.10" } }, - "node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/parse-statements": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", @@ -33458,17 +29379,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -34015,20 +29925,6 @@ "node": ">=8" } }, - "node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -35147,20 +31043,6 @@ "dev": true, "license": "MIT" }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -35850,13 +31732,6 @@ "node": ">= 14" } }, - "node_modules/sorted-array-functions": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", - "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==", - "dev": true, - "license": "MIT" - }, "node_modules/source-map": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", @@ -36047,13 +31922,6 @@ "node": ">=8" } }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "dev": true, - "license": "MIT" - }, "node_modules/standard-as-callback": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", @@ -36826,14 +32694,14 @@ } }, "node_modules/tcp-port-used": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", - "integrity": "sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.3.tgz", + "integrity": "sha512-4CEQ3qRJYo+mtEbJ+OoQu3dF4TDkwaO3RDVC4UzP5cpAOIUWwuwPjD7sdxDFFqsMUjsXVVYBMlg/boAaloThMA==", "dev": true, "license": "MIT", "dependencies": { "debug": "4.3.1", - "is2": "^2.0.6" + "is2": "2.0.1" } }, "node_modules/tcp-port-used/node_modules/debug": { @@ -37105,9 +32973,9 @@ "peer": true }, "node_modules/tmp": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.6.tgz", - "integrity": "sha512-5sJPdPjfI5Kx+qbrDesxkglRBxW//g7hCsqspEjwkewGvBMGIKMOTKzLt1hFVJzyadba3lDUN20O9qhvbQUSTA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", "engines": { @@ -37287,24 +33155,28 @@ } }, "node_modules/ts-checker-rspack-plugin": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/ts-checker-rspack-plugin/-/ts-checker-rspack-plugin-1.3.1.tgz", - "integrity": "sha512-4VBjKblnJwypq+2aWZ9V65HENAmU/2s04d717YhLjC65MKitTTnqKeHE6GGB5C4S+2BnqZ9MtJt5AvS7nldaLQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ts-checker-rspack-plugin/-/ts-checker-rspack-plugin-1.5.1.tgz", + "integrity": "sha512-h0jF3PAIrG+UA2nZ2OPUEt0NywkpiJkydkoVg/Kd1OFPFlrayNBJoSP0zeNPUpdsFEt7ICX1BjENCfOH9nmMyA==", "dev": true, "license": "MIT", "dependencies": { - "@rspack/lite-tapable": "^1.1.0", + "@rspack/lite-tapable": "^1.1.2", "chokidar": "^3.6.0", - "memfs": "^4.57.2", + "memfs": "^4.57.7", "picocolors": "^1.1.1" }, "peerDependencies": { - "@rspack/core": "^1.0.0 || ^2.0.0-0", + "@rspack/core": "^1.0.0 || ^2.0.0", + "@typescript/native-preview": "^7.0.0-0", "typescript": ">=3.8.0" }, "peerDependenciesMeta": { "@rspack/core": { "optional": true + }, + "@typescript/native-preview": { + "optional": true } } }, @@ -37343,14 +33215,14 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/@jsonjoy.com/fs-core": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.3.tgz", - "integrity": "sha512-IvO50vkGydDZwS1e9rz/JXEtCCt9XvqxoGI6FlrVIvVm4/HpygMKW4ETtREWtMTsN5CLJ9FR6GuCduoQPZLBiw==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.8.tgz", + "integrity": "sha512-YzVbwggV9452VCeHgo0bjsTaUt1O7JE0XpEsPar93nn/+RAwXk0mb1Y+f5EDJ3TRtRCFe+Ck5RuojdfB4jeHVw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.3", - "@jsonjoy.com/fs-node-utils": "4.57.3", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8", "thingies": "^2.5.0" }, "engines": { @@ -37365,15 +33237,15 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/@jsonjoy.com/fs-fsa": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.3.tgz", - "integrity": "sha512-JlIDGUWPl7Y6zl+/ISnZuh8z2aMr/xoR66D18zlaVAuL192CvlNJEzOlzp27x4P52HRtDnCSOk6f59vTsmp5vw==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.8.tgz", + "integrity": "sha512-vmClyvCQMxgqz7uamDiGtRfp4MjzOznk3pcQjCxlIwJcw7TWeyr+bF30hI0x8NxdtNOGMg1pHM74VDIXOeyjuw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.3", - "@jsonjoy.com/fs-node-builtins": "4.57.3", - "@jsonjoy.com/fs-node-utils": "4.57.3", + "@jsonjoy.com/fs-core": "4.57.8", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8", "thingies": "^2.5.0" }, "engines": { @@ -37388,17 +33260,17 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/@jsonjoy.com/fs-node": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.3.tgz", - "integrity": "sha512-089gZoKvbeOsT2jeBaVKSz91oFXQWFG7a62sMY6gVMHnoWbyGzTb6OVUP/V7G3wLQLJ555BEsHt8SD1nj1dgaQ==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.8.tgz", + "integrity": "sha512-IPEOlDYSnTDYpjQlQg2F8h+eqxKQN3sdbroI0WrteRiQZ462HzVpBo9ZZX485njz4nAacoe3fd4iDiIhk+k5Hg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.3", - "@jsonjoy.com/fs-node-builtins": "4.57.3", - "@jsonjoy.com/fs-node-utils": "4.57.3", - "@jsonjoy.com/fs-print": "4.57.3", - "@jsonjoy.com/fs-snapshot": "4.57.3", + "@jsonjoy.com/fs-core": "4.57.8", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8", + "@jsonjoy.com/fs-print": "4.57.8", + "@jsonjoy.com/fs-snapshot": "4.57.8", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, @@ -37414,9 +33286,9 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/@jsonjoy.com/fs-node-builtins": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.3.tgz", - "integrity": "sha512-JAI3PqNuY8BR7ovy4h0bADLrqJLIcUauONNZfyTxUnj3Wf3tpTYe39eJ6z7FzYyA+tdMt33VpiQQUikGr3QOBw==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.8.tgz", + "integrity": "sha512-mxXSXw8zZwRVakcjLqR2I/psy4gURFSASZS10kKJ2kJw05GC2nXGroGrWVHxwgkxXgQLsFQnB74QaLzsxzdL/w==", "dev": true, "license": "Apache-2.0", "engines": { @@ -37431,15 +33303,15 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/@jsonjoy.com/fs-node-to-fsa": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.3.tgz", - "integrity": "sha512-uZGxyC0zDmcmW5bfHd4YivAZ54BLlbF9G0K5rBaksI/tZdJSGM7/AC+1TY7yvFu0Wc6gUHR7mFwf6SbQ3J1BTQ==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.8.tgz", + "integrity": "sha512-AWZcT/4+H+iDl4XCukbXrarvwEgOrf/prFI5/7eg4ix9FxqVsZysIDJd1Kjd+AjlCeHKHJOaRqjLd5HiGSCJEw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-fsa": "4.57.3", - "@jsonjoy.com/fs-node-builtins": "4.57.3", - "@jsonjoy.com/fs-node-utils": "4.57.3" + "@jsonjoy.com/fs-fsa": "4.57.8", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8" }, "engines": { "node": ">=10.0" @@ -37453,13 +33325,13 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/@jsonjoy.com/fs-node-utils": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.3.tgz", - "integrity": "sha512-quCil8AvfcOxob4pn0drGdcQWpkPVgkt9q1+EjeyXXT40/L3l5lvYrr6hR8LmHu0eg+DNNaUwqjLT6Hr7V4sdQ==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.8.tgz", + "integrity": "sha512-E/bJ7sQAb4pu9nbeJhbULU3WnqWrswte4N9Js/oHt7aHB746S8/XBqKlcbrqIgnD3095XluovNEZuu5ONT230g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.3" + "@jsonjoy.com/fs-node-builtins": "4.57.8" }, "engines": { "node": ">=10.0" @@ -37473,13 +33345,13 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/@jsonjoy.com/fs-print": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.3.tgz", - "integrity": "sha512-ITwaLZpGIqD9jHndwMvDFZDIvbVzGRsJZDQ5HKln0vyMculu1c1nb7zbEBgY8BVSBZ9S2xO138OWIBGeRsrF3Q==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.8.tgz", + "integrity": "sha512-DfzhOBpmvNu5P/KSe4NNQaOnvNliTdcf0qrh/4EReErF/XUQXYkd0vZl/OiJCm/qjEEo8DWRstliw2/JNS84dA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-utils": "4.57.3", + "@jsonjoy.com/fs-node-utils": "4.57.8", "tree-dump": "^1.1.0" }, "engines": { @@ -37494,14 +33366,14 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/@jsonjoy.com/fs-snapshot": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.3.tgz", - "integrity": "sha512-wdNaG2DxCtvj9lKldAnEV3ycYPEpk+p2cP2lHD1qdxkoQGlWUtQverqvG9KZSkm6BHFha4PP6XRZbpARNfHRxA==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.8.tgz", + "integrity": "sha512-L+eqKaWOHLDaiMv1dh/EWQ4hA+o6xAhWSumTo3Teg7OM18jU/KE13/e8Mfal+eAZ/pSl4wIhKHcDiwapJzC8Wg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", - "@jsonjoy.com/fs-node-utils": "4.57.3", + "@jsonjoy.com/fs-node-utils": "4.57.8", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, @@ -37605,6 +33477,13 @@ "tslib": "2" } }, + "node_modules/ts-checker-rspack-plugin/node_modules/@rspack/lite-tapable": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rspack/lite-tapable/-/lite-tapable-1.1.2.tgz", + "integrity": "sha512-1OnyWChLGE46YzWyjlmYJssOu/Y0STAnnr2ueKPqDCYTf63GJMs0mxNnCul4dNiVqHYPKv3/fxrTY3IpqoVwZQ==", + "dev": true, + "license": "MIT" + }, "node_modules/ts-checker-rspack-plugin/node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -37644,20 +33523,20 @@ } }, "node_modules/ts-checker-rspack-plugin/node_modules/memfs": { - "version": "4.57.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.3.tgz", - "integrity": "sha512-dlvqataP1zUOlfj6pv9wgCSC5pRIooNntXgdLfR7FWlcKi1p8fMfJADtHp/+8Dhu5JFvMHNh7L0QVcuaaBKqqA==", + "version": "4.57.8", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.8.tgz", + "integrity": "sha512-bApYhn8BLpFAnAQmFfEl/NPN+8qx5Ar3V4Qt3ek23mVwBEElzV7c6XoPkb/PCG8ZFpowCEpHcPwMFTwHS7tSMA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.3", - "@jsonjoy.com/fs-fsa": "4.57.3", - "@jsonjoy.com/fs-node": "4.57.3", - "@jsonjoy.com/fs-node-builtins": "4.57.3", - "@jsonjoy.com/fs-node-to-fsa": "4.57.3", - "@jsonjoy.com/fs-node-utils": "4.57.3", - "@jsonjoy.com/fs-print": "4.57.3", - "@jsonjoy.com/fs-snapshot": "4.57.3", + "@jsonjoy.com/fs-core": "4.57.8", + "@jsonjoy.com/fs-fsa": "4.57.8", + "@jsonjoy.com/fs-node": "4.57.8", + "@jsonjoy.com/fs-node-builtins": "4.57.8", + "@jsonjoy.com/fs-node-to-fsa": "4.57.8", + "@jsonjoy.com/fs-node-utils": "4.57.8", + "@jsonjoy.com/fs-print": "4.57.8", + "@jsonjoy.com/fs-snapshot": "4.57.8", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", @@ -37776,16 +33655,14 @@ } }, "node_modules/ts-loader": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.0.tgz", - "integrity": "sha512-dsJO0S+T7grTDWTc4a0nTygXGjKncVUpx8Y+af8EvI/D5WgTJby5UEk5eoMCB9EcLQmnvitqh99MqtjtHgAwFQ==", + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.6.2.tgz", + "integrity": "sha512-R4iuczmtgxvtuI556s+hTZ6/7Ee03VCAk/l/M8LY1OAsUgB7YydsCxkgq9D9pKRaD7GJqUi2u8fp9zZP/ufjKA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", + "picomatch": "^4.0.0", "source-map": "^0.7.4" }, "engines": { @@ -38326,17 +34203,6 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, - "node_modules/upath": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", - "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4", - "yarn": "*" - } - }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", diff --git a/package.json b/package.json index d74b5e787..065eea6ec 100644 --- a/package.json +++ b/package.json @@ -159,16 +159,16 @@ "@eslint/js": "9.35.0", "@nestjs/schematics": "11.1.0", "@nestjs/testing": "11.1.21", - "@nx/angular": "22.7.5", - "@nx/eslint-plugin": "22.7.5", - "@nx/jest": "22.7.5", - "@nx/js": "22.7.5", - "@nx/module-federation": "22.7.5", - "@nx/nest": "22.7.5", - "@nx/node": "22.7.5", - "@nx/storybook": "22.7.5", - "@nx/web": "22.7.5", - "@nx/workspace": "22.7.5", + "@nx/angular": "23.0.1", + "@nx/eslint-plugin": "23.0.1", + "@nx/jest": "23.0.1", + "@nx/js": "23.0.1", + "@nx/module-federation": "23.0.1", + "@nx/nest": "23.0.1", + "@nx/node": "23.0.1", + "@nx/storybook": "23.0.1", + "@nx/web": "23.0.1", + "@nx/workspace": "23.0.1", "@schematics/angular": "21.2.6", "@storybook/addon-docs": "10.1.10", "@storybook/addon-themes": "10.1.10", @@ -192,10 +192,10 @@ "eslint-plugin-import": "2.32.0", "eslint-plugin-storybook": "10.1.10", "husky": "9.1.7", - "jest": "30.2.0", + "jest": "30.3.0", "jest-environment-jsdom": "30.2.0", "jest-preset-angular": "16.0.0", - "nx": "22.7.5", + "nx": "23.0.1", "prettier": "3.8.4", "prettier-plugin-organize-attributes": "1.0.0", "prisma": "7.8.0", From 830c1e229573bc0cdaffcb331e823c568a6f578e Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 26 Jun 2026 21:02:24 +0200 Subject: [PATCH 58/60] Task/improve data source encoding (#7138) Improve data source encoding --- apps/api/src/app/info/info.service.ts | 2 +- apps/api/src/helper/data-source.helper.ts | 54 +++++++++++++++++++ ...form-data-source-in-request.interceptor.ts | 2 +- ...orm-data-source-in-response.interceptor.ts | 16 ++++-- libs/common/src/lib/helper.ts | 17 ------ 5 files changed, 69 insertions(+), 22 deletions(-) diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index 86630db53..4441036a2 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -1,6 +1,7 @@ import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; import { SubscriptionService } from '@ghostfolio/api/app/subscription/subscription.service'; import { UserService } from '@ghostfolio/api/app/user/user.service'; +import { encodeDataSource } from '@ghostfolio/api/helper/data-source.helper'; import { BenchmarkService } from '@ghostfolio/api/services/benchmark/benchmark.service'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; @@ -17,7 +18,6 @@ import { PROPERTY_UPTIME, ghostfolioFearAndGreedIndexDataSourceStocks } from '@ghostfolio/common/config'; -import { encodeDataSource } from '@ghostfolio/common/helper'; import { InfoItem, Statistics } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; diff --git a/apps/api/src/helper/data-source.helper.ts b/apps/api/src/helper/data-source.helper.ts index c4a55c9da..f3ed75229 100644 --- a/apps/api/src/helper/data-source.helper.ts +++ b/apps/api/src/helper/data-source.helper.ts @@ -1,4 +1,58 @@ import { DataSource } from '@prisma/client'; +import { createHash } from 'node:crypto'; + +const encodedDataSourceByDataSource = new Map( + Object.values(DataSource).map((dataSource) => { + return [dataSource, hashDataSource(dataSource)]; + }) +); + +const dataSourceByEncodedDataSource = new Map( + [...encodedDataSourceByDataSource].map(([dataSource, encodedDataSource]) => { + return [encodedDataSource, dataSource]; + }) +); + +/** + * @deprecated Backward compatibility to support importing data that was + * exported using the previous data source encoding + */ +const dataSourceByDeprecatedEncodedDataSource = new Map( + Object.values(DataSource).map((dataSource) => { + return [deprecatedHashDataSource(dataSource), dataSource]; + }) +); + +/** + * @deprecated Backward compatibility (see above) + */ +function deprecatedHashDataSource(dataSource: DataSource) { + return Buffer.from(dataSource, 'utf-8').toString('hex'); +} + +function hashDataSource(dataSource: DataSource) { + return createHash('sha256').update(dataSource).digest('hex').slice(0, 8); +} + +export function decodeDataSource(encodedDataSource: string) { + if (!encodedDataSource) { + return undefined; + } + + return ( + dataSourceByEncodedDataSource.get(encodedDataSource) ?? + dataSourceByDeprecatedEncodedDataSource.get(encodedDataSource) ?? + encodedDataSource + ); +} + +export function encodeDataSource(dataSource: DataSource) { + if (!dataSource) { + return undefined; + } + + return encodedDataSourceByDataSource.get(dataSource); +} export function getMaskedGhostfolioDataSource({ dataSource, diff --git a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts index 17c5ebe57..4ab0794ec 100644 --- a/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-request/transform-data-source-in-request.interceptor.ts @@ -1,5 +1,5 @@ +import { decodeDataSource } from '@ghostfolio/api/helper/data-source.helper'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; -import { decodeDataSource } from '@ghostfolio/common/helper'; import { CallHandler, diff --git a/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts b/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts index ddcf30b0c..56d260d4d 100644 --- a/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts +++ b/apps/api/src/interceptors/transform-data-source-in-response/transform-data-source-in-response.interceptor.ts @@ -1,7 +1,10 @@ -import { getMaskedGhostfolioDataSource } from '@ghostfolio/api/helper/data-source.helper'; +import { + encodeDataSource, + getMaskedGhostfolioDataSource +} from '@ghostfolio/api/helper/data-source.helper'; import { redactPaths } from '@ghostfolio/api/helper/object.helper'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; -import { encodeDataSource } from '@ghostfolio/common/helper'; +import { hasRole } from '@ghostfolio/common/permissions'; import { CallHandler, @@ -45,11 +48,14 @@ export class TransformDataSourceInResponseInterceptor< next: CallHandler ): Observable { const isExportMode = context.getClass().name === 'ExportController'; + const { user } = context.switchToHttp().getRequest(); return next.handle().pipe( map((data: any) => { if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { - const valueMap = this.encodedDataSourceMap; + const valueMap = hasRole(user, 'ADMIN') + ? {} + : { ...this.encodedDataSourceMap }; if (isExportMode) { const ghostfolioDataSources = this.configurationService.get( @@ -64,6 +70,10 @@ export class TransformDataSourceInResponseInterceptor< } } + if (Object.keys(valueMap).length === 0) { + return data; + } + data = redactPaths({ valueMap, object: data, diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index 3219ed553..58e62618b 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -1,7 +1,6 @@ import { NumberParser } from '@internationalized/number'; import { Type as ActivityType, - DataSource, MarketData, Prisma, SymbolProfile, @@ -166,14 +165,6 @@ export function capitalize(aString: string) { return aString.charAt(0).toUpperCase() + aString.slice(1).toLowerCase(); } -export function decodeDataSource(encodedDataSource: string) { - if (encodedDataSource) { - return Buffer.from(encodedDataSource, 'hex').toString(); - } - - return undefined; -} - export function downloadAsFile({ content, contentType = 'text/plain', @@ -199,14 +190,6 @@ export function downloadAsFile({ a.click(); } -export function encodeDataSource(aDataSource: DataSource) { - if (aDataSource) { - return Buffer.from(aDataSource, 'utf-8').toString('hex'); - } - - return undefined; -} - export function extractNumberFromString({ locale = 'en-US', value From 55c9c3d39f75d268bea46a7b9fa208264ddfe965 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 26 Jun 2026 21:02:46 +0200 Subject: [PATCH 59/60] Task/improve error message styling in import activities dialog (#7143) * Improve styling * Update changelog --- CHANGELOG.md | 1 + .../import-activities-dialog.html | 8 +++++--- .../import-activities-dialog.scss | 11 +++++++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 836ee7683..84aa776b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Improved the error message styling in the import activities dialog - Improved the grantee display in the access table to share the portfolio - Improved the country mapping for data providers - Upgraded `bull-board` from version `7.2.1` to `8.0.1` diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html index 506076afd..4149ce5b6 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.html @@ -164,12 +164,14 @@ @for (message of errorMessages; track message; let i = $index) { - -
+ +
-
{{ message }}
+
+ {{ message }} +
diff --git a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.scss b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.scss index 0f3b181cc..79f8f868c 100644 --- a/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.scss +++ b/apps/client/src/app/pages/portfolio/activities/import-activities-dialog/import-activities-dialog.scss @@ -1,3 +1,5 @@ +@use '@angular/material' as mat; + :host { display: block; @@ -41,8 +43,13 @@ } .mat-expansion-panel { - background: none; - box-shadow: none; + @include mat.expansion-overrides( + ( + container-background-color: transparent, + container-elevation-shadow: none, + container-shape: 0 + ) + ); .mat-expansion-panel-header { color: inherit; From 038d17edb761f696ad55666c9fd8c53b82dbe5b5 Mon Sep 17 00:00:00 2001 From: Thomas Kaul <4159106+dtslvr@users.noreply.github.com> Date: Fri, 26 Jun 2026 21:10:18 +0200 Subject: [PATCH 60/60] Release 3.17.0 (#7144) --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84aa776b1..051addbb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 3.17.0 - 2026-06-26 ### Added diff --git a/package-lock.json b/package-lock.json index dbd9ca885..7eef6ba51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "3.16.0", + "version": "3.17.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "3.16.0", + "version": "3.17.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { diff --git a/package.json b/package.json index 065eea6ec..95eabcb9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "3.16.0", + "version": "3.17.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio",