diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bdd653a9..33f1064ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -94,6 +94,129 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 2.115.0 - 2024-10-14 +### Changed + +- Extended the assistant by a holding selector +- Separated the _FIRE_ / _X-ray_ page +- Improved the language localization for Italian (`it`) +- Upgraded `ngx-skeleton-loader` from version `7.0.0` to `9.0.0` + +## 2.122.0 - 2024-11-07 + +### Changed + +- Upgraded `countries-list` from version `3.1.0` to `3.1.1` + +### Fixed + +- Fixed an issue with the algebraic sign in the chart of the holdings tab on the home page (experimental) +- Improved the exception handling in the user authorization service +- Disabled the caching of the benchmarks in the markets overview if sharing the _Fear & Greed Index_ (market mood) is enabled + +## 2.121.1 - 2024-11-02 + +### Added + +- Set the stack and container names in the `docker-compose` files (`docker-compose.yml`, `docker-compose.build.yml` and `docker-compose.dev.yml`) + +### Changed + +- Reverted the permissions (`chmod 0700`) on `entrypoint.sh` in the `Dockerfile` +- Upgraded the _Stripe_ dependencies + +## 2.120.0 - 2024-10-30 + +### Added + +- Added support for log levels (`LOG_LEVELS`) to conditionally log `prisma` query events (`debug` or `verbose`) + +### Changed + +- Restructured the resources page +- Renamed the static portfolio analysis rule from _Allocation Cluster Risk_ to _Economic Market Cluster Risk_ (Developed Markets and Emerging Markets) +- Improved the language localization for German (`de`) +- Switched the `consistent-generic-constructors` rule from `warn` to `error` in the `eslint` configuration +- Switched the `consistent-indexed-object-style` rule from `warn` to `off` in the `eslint` configuration +- Switched the `consistent-type-assertions` rule from `warn` to `error` in the `eslint` configuration +- Switched the `prefer-optional-chain` rule from `warn` to `error` in the `eslint` configuration +- Upgraded `Nx` from version `20.0.3` to `20.0.6` + +## 2.119.0 - 2024-10-26 + +### Changed + +- Switched the `consistent-type-definitions` rule from `warn` to `error` in the `eslint` configuration +- Switched the `no-empty-function` rule from `warn` to `error` in the `eslint` configuration +- Switched the `prefer-function-type` rule from `warn` to `error` in the `eslint` configuration +- Upgraded `prisma` from version `5.20.0` to `5.21.1` + +### Fixed + +- Fixed an issue with the X-axis scale of the dividend timeline on the analysis page +- Fixed an issue with the X-axis scale of the investment timeline on the analysis page +- Fixed an issue with the X-axis scale of the portfolio evolution chart on the analysis page +- Fixed an issue in the calculation of the static portfolio analysis rule: _Allocation Cluster Risk_ (Developed Markets) +- Fixed an issue in the calculation of the static portfolio analysis rule: _Allocation Cluster Risk_ (Emerging Markets) + +## 2.118.0 - 2024-10-23 + +### Added + +- Added a new static portfolio analysis rule: _Allocation Cluster Risk_ (Developed Markets) +- Added a new static portfolio analysis rule: _Allocation Cluster Risk_ (Emerging Markets) +- Added support for mutual funds in the _EOD Historical Data_ service + +### Changed + +- Improved the font colors of the chart of the holdings tab on the home page (experimental) +- Optimized the dialog sizes for mobile (full screen) +- Optimized the git-hook via `husky` to lint only affected projects before a commit +- Upgraded `angular` from version `18.1.1` to `18.2.8` +- Upgraded `Nx` from version `19.5.6` to `20.0.3` + +### Fixed + +- Fixed the warning `export was not found` in connection with `GetValuesParams` +- Quoted the password for the _Redis_ service `healthcheck` in the `docker-compose` files (`docker-compose.yml` and `docker-compose.build.yml`) + +## 2.117.0 - 2024-10-19 + +### Added + +- Added the logotype to the footer +- Added the data providers management to the admin control panel + +### Changed + +- Improved the backgrounds of the chart of the holdings tab on the home page (experimental) +- Improved the language localization for German (`de`) + +### Fixed + +- Fixed an issue in the carousel component for the testimonial section on the landing page + +## 2.116.0 - 2024-10-17 + +### Added + +- Extended the content of the _Self-Hosting_ section by the benchmarks concept for _Compare with..._ on the Frequently Asked Questions (FAQ) page +- Extended the content of the _Self-Hosting_ section by the benchmarks concept for _Markets_ on the Frequently Asked Questions (FAQ) page +- Set the permissions (`chmod 0700`) on `entrypoint.sh` in the `Dockerfile` + +### Changed + +- Improved the empty state in the benchmarks of the markets overview +- Disabled the text hover effect in the chart of the holdings tab on the home page (experimental) +- Improved the usability to customize the rule thresholds in the _X-ray_ section by introducing units (experimental) +- Switched to adjusted market prices (splits and dividends) in the get historical functionality of the _EOD Historical Data_ service +- Improved the language localization for German (`de`) + +### Fixed + +- Fixed the usage of the environment variable `PROCESSOR_PORTFOLIO_SNAPSHOT_COMPUTATION_CONCURRENCY` + +## 2.115.0 - 2024-10-14 + ### Added - Added the name to the tooltip of the chart of the holdings tab on the home page (experimental) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 18441dda7..112d774e7 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -648,7 +648,7 @@ export class AdminService { } private async getUsersWithAnalytics(): Promise { - let orderBy: any = { + let orderBy: Prisma.UserOrderByWithRelationInput = { createdAt: 'desc' }; let where: Prisma.UserWhereInput; @@ -656,7 +656,7 @@ export class AdminService { if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { orderBy = { Analytics: { - updatedAt: 'desc' + lastRequestAt: 'desc' } }; where = { diff --git a/apps/api/src/app/auth/jwt.strategy.ts b/apps/api/src/app/auth/jwt.strategy.ts index a8ad8fd08..7a3fb224b 100644 --- a/apps/api/src/app/auth/jwt.strategy.ts +++ b/apps/api/src/app/auth/jwt.strategy.ts @@ -46,7 +46,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { update: { country, activityCount: { increment: 1 }, - updatedAt: new Date() + lastRequestAt: new Date() }, where: { userId: user.id } }); @@ -60,7 +60,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { ); } } catch (error) { - if (error?.getStatus() === StatusCodes.TOO_MANY_REQUESTS) { + if (error?.getStatus?.() === StatusCodes.TOO_MANY_REQUESTS) { throw error; } else { throw new HttpException( diff --git a/apps/api/src/app/benchmark/benchmark.service.ts b/apps/api/src/app/benchmark/benchmark.service.ts index 36f196842..a659281d7 100644 --- a/apps/api/src/app/benchmark/benchmark.service.ts +++ b/apps/api/src/app/benchmark/benchmark.service.ts @@ -437,7 +437,7 @@ export class BenchmarkService { }; }); - if (storeInCache) { + if (!enableSharing && storeInCache) { const expiration = addHours(new Date(), 2); await this.redisCacheService.set( diff --git a/apps/api/src/app/info/info.service.ts b/apps/api/src/app/info/info.service.ts index bd291c511..62a78d1d8 100644 --- a/apps/api/src/app/info/info.service.ts +++ b/apps/api/src/app/info/info.service.ts @@ -23,10 +23,10 @@ import { import { InfoItem, Statistics, - Subscription + SubscriptionOffer } from '@ghostfolio/common/interfaces'; import { permissions } from '@ghostfolio/common/permissions'; -import { SubscriptionOffer } from '@ghostfolio/common/types'; +import { SubscriptionOfferKey } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; @@ -101,7 +101,7 @@ export class InfoService { isUserSignupEnabled, platforms, statistics, - subscriptions + subscriptionOffers ] = await Promise.all([ this.benchmarkService.getBenchmarkAssetProfiles(), this.getDemoAuthToken(), @@ -110,7 +110,7 @@ export class InfoService { orderBy: { name: 'asc' } }), this.getStatistics(), - this.getSubscriptions() + this.getSubscriptionOffers() ]); if (isUserSignupEnabled) { @@ -125,7 +125,7 @@ export class InfoService { isReadOnlyMode, platforms, statistics, - subscriptions, + subscriptionOffers, baseCurrency: DEFAULT_CURRENCY, currencies: this.exchangeRateDataService.getCurrencies() }; @@ -142,7 +142,7 @@ export class InfoService { }, { Analytics: { - updatedAt: { + lastRequestAt: { gt: subDays(new Date(), aDays) } } @@ -314,8 +314,8 @@ export class InfoService { return statistics; } - private async getSubscriptions(): Promise<{ - [offer in SubscriptionOffer]: Subscription; + private async getSubscriptionOffers(): Promise<{ + [offer in SubscriptionOfferKey]: SubscriptionOffer; }> { if (!this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { return undefined; diff --git a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts index 30f6ec264..bb83b98a8 100644 --- a/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts +++ b/apps/api/src/app/portfolio/calculator/portfolio-calculator.ts @@ -162,10 +162,6 @@ export abstract class PortfolioCalculator { this.snapshotPromise = this.initialize(); } - protected abstract calculateOverallPerformance( - positions: TimelinePosition[] - ): PortfolioSnapshot; - @LogPerformance public async computeSnapshot(): Promise { const lastTransactionPoint = last(this.transactionPoints); @@ -769,62 +765,6 @@ export abstract class PortfolioCalculator { return { chart }; } - @LogPerformance - public async getSnapshot() { - await this.snapshotPromise; - - return this.snapshot; - } - - public getStartDate() { - let firstAccountBalanceDate: Date; - let firstActivityDate: Date; - - try { - const firstAccountBalanceDateString = first( - this.accountBalanceItems - )?.date; - firstAccountBalanceDate = firstAccountBalanceDateString - ? parseDate(firstAccountBalanceDateString) - : new Date(); - } catch (error) { - firstAccountBalanceDate = new Date(); - } - - try { - const firstActivityDateString = this.transactionPoints[0].date; - firstActivityDate = firstActivityDateString - ? parseDate(firstActivityDateString) - : new Date(); - } catch (error) { - firstActivityDate = new Date(); - } - - return min([firstAccountBalanceDate, firstActivityDate]); - } - - protected abstract getSymbolMetrics({ - chartDateMap, - dataSource, - end, - exchangeRates, - marketSymbolMap, - start, - symbol - }: { - chartDateMap: { [date: string]: boolean }; - end: Date; - exchangeRates: { [dateString: string]: number }; - marketSymbolMap: { - [date: string]: { [symbol: string]: Big }; - }; - start: Date; - } & AssetProfileIdentifier): SymbolMetrics; - - public getTransactionPoints() { - return this.transactionPoints; - } - @LogPerformance public async getValuablesInBaseCurrency() { await this.snapshotPromise; @@ -832,72 +772,11 @@ export abstract class PortfolioCalculator { return this.snapshot.totalValuablesWithCurrencyEffect; } - private getChartDateMap({ - endDate, - startDate, - step - }: { - endDate: Date; - startDate: Date; - step: number; - }): { [date: string]: true } { - // Create a map of all relevant chart dates: - // 1. Add transaction point dates - const chartDateMap = this.transactionPoints.reduce((result, { date }) => { - result[date] = true; - return result; - }, {}); - - // 2. Add dates between transactions respecting the specified step size - for (const date of eachDayOfInterval( - { end: endDate, start: startDate }, - { step } - )) { - chartDateMap[format(date, DATE_FORMAT)] = true; - } - - if (step > 1) { - // Reduce the step size of last 90 days - for (const date of eachDayOfInterval( - { end: endDate, start: subDays(endDate, 90) }, - { step: 3 } - )) { - chartDateMap[format(date, DATE_FORMAT)] = true; - } - - // Reduce the step size of last 30 days - for (const date of eachDayOfInterval( - { end: endDate, start: subDays(endDate, 30) }, - { step: 1 } - )) { - chartDateMap[format(date, DATE_FORMAT)] = true; - } - } - - // Make sure the end date is present - chartDateMap[format(endDate, DATE_FORMAT)] = true; - - // Make sure some key dates are present - for (const dateRange of ['1d', '1y', '5y', 'max', 'mtd', 'wtd', 'ytd']) { - const { endDate: dateRangeEnd, startDate: dateRangeStart } = - getIntervalFromDateRange(dateRange); - - if ( - !isBefore(dateRangeStart, startDate) && - !isAfter(dateRangeStart, endDate) - ) { - chartDateMap[format(dateRangeStart, DATE_FORMAT)] = true; - } - - if ( - !isBefore(dateRangeEnd, startDate) && - !isAfter(dateRangeEnd, endDate) - ) { - chartDateMap[format(dateRangeEnd, DATE_FORMAT)] = true; - } - } + @LogPerformance + public async getSnapshot() { + await this.snapshotPromise; - return chartDateMap; + return this.snapshot; } @LogPerformance @@ -1120,4 +999,125 @@ export abstract class PortfolioCalculator { await this.initialize(); } } + + public getStartDate() { + let firstAccountBalanceDate: Date; + let firstActivityDate: Date; + + try { + const firstAccountBalanceDateString = first( + this.accountBalanceItems + )?.date; + firstAccountBalanceDate = firstAccountBalanceDateString + ? parseDate(firstAccountBalanceDateString) + : new Date(); + } catch (error) { + firstAccountBalanceDate = new Date(); + } + + try { + const firstActivityDateString = this.transactionPoints[0].date; + firstActivityDate = firstActivityDateString + ? parseDate(firstActivityDateString) + : new Date(); + } catch (error) { + firstActivityDate = new Date(); + } + + return min([firstAccountBalanceDate, firstActivityDate]); + } + + public getTransactionPoints() { + return this.transactionPoints; + } + + private getChartDateMap({ + endDate, + startDate, + step + }: { + endDate: Date; + startDate: Date; + step: number; + }): { [date: string]: true } { + // Create a map of all relevant chart dates: + // 1. Add transaction point dates + const chartDateMap = this.transactionPoints.reduce((result, { date }) => { + result[date] = true; + return result; + }, {}); + + // 2. Add dates between transactions respecting the specified step size + for (const date of eachDayOfInterval( + { end: endDate, start: startDate }, + { step } + )) { + chartDateMap[format(date, DATE_FORMAT)] = true; + } + + if (step > 1) { + // Reduce the step size of last 90 days + for (const date of eachDayOfInterval( + { end: endDate, start: subDays(endDate, 90) }, + { step: 3 } + )) { + chartDateMap[format(date, DATE_FORMAT)] = true; + } + + // Reduce the step size of last 30 days + for (const date of eachDayOfInterval( + { end: endDate, start: subDays(endDate, 30) }, + { step: 1 } + )) { + chartDateMap[format(date, DATE_FORMAT)] = true; + } + } + + // Make sure the end date is present + chartDateMap[format(endDate, DATE_FORMAT)] = true; + + // Make sure some key dates are present + for (const dateRange of ['1d', '1y', '5y', 'max', 'mtd', 'wtd', 'ytd']) { + const { endDate: dateRangeEnd, startDate: dateRangeStart } = + getIntervalFromDateRange(dateRange); + + if ( + !isBefore(dateRangeStart, startDate) && + !isAfter(dateRangeStart, endDate) + ) { + chartDateMap[format(dateRangeStart, DATE_FORMAT)] = true; + } + + if ( + !isBefore(dateRangeEnd, startDate) && + !isAfter(dateRangeEnd, endDate) + ) { + chartDateMap[format(dateRangeEnd, DATE_FORMAT)] = true; + } + } + + return chartDateMap; + } + + protected abstract getSymbolMetrics({ + chartDateMap, + dataSource, + end, + exchangeRates, + marketSymbolMap, + start, + symbol + }: { + chartDateMap: { [date: string]: boolean }; + end: Date; + exchangeRates: { [dateString: string]: number }; + marketSymbolMap: { + [date: string]: { [symbol: string]: Big }; + }; + start: Date; + } & AssetProfileIdentifier): SymbolMetrics; + + protected abstract calculateOverallPerformance( + positions: TimelinePosition[] + ): PortfolioSnapshot; } diff --git a/apps/api/src/app/portfolio/current-rate.service.ts b/apps/api/src/app/portfolio/current-rate.service.ts index cd1994826..ab7bf2ebf 100644 --- a/apps/api/src/app/portfolio/current-rate.service.ts +++ b/apps/api/src/app/portfolio/current-rate.service.ts @@ -52,27 +52,24 @@ export class CurrentRateService { .then((dataResultProvider) => { const result: GetValueObject[] = []; - for (const dataGatheringItem of dataGatheringItems) { - if ( - dataResultProvider?.[dataGatheringItem.symbol]?.dataProviderInfo - ) { + for (const { dataSource, symbol } of dataGatheringItems) { + if (dataResultProvider?.[symbol]?.dataProviderInfo) { dataProviderInfos.push( - dataResultProvider[dataGatheringItem.symbol].dataProviderInfo + dataResultProvider[symbol].dataProviderInfo ); } - if (dataResultProvider?.[dataGatheringItem.symbol]?.marketPrice) { + if (dataResultProvider?.[symbol]?.marketPrice) { result.push({ - dataSource: dataGatheringItem.dataSource, + dataSource, + symbol, date: today, - marketPrice: - dataResultProvider?.[dataGatheringItem.symbol]?.marketPrice, - symbol: dataGatheringItem.symbol + marketPrice: dataResultProvider?.[symbol]?.marketPrice }); } else { quoteErrors.push({ - dataSource: dataGatheringItem.dataSource, - symbol: dataGatheringItem.symbol + dataSource, + symbol }); } } diff --git a/apps/api/src/app/portfolio/portfolio.controller.ts b/apps/api/src/app/portfolio/portfolio.controller.ts index fe42c9838..e33027fb6 100644 --- a/apps/api/src/app/portfolio/portfolio.controller.ts +++ b/apps/api/src/app/portfolio/portfolio.controller.ts @@ -77,12 +77,15 @@ export class PortfolioController { @Get('details') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(RedactValuesInResponseInterceptor) + @UseInterceptors(TransformDataSourceInRequestInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor) public async getDetails( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Query('accounts') filterByAccounts?: string, @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, @Query('range') dateRange: DateRange = 'max', + @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string, @Query('withMarkets') withMarketsParam = 'false' ): Promise { @@ -98,6 +101,8 @@ export class PortfolioController { const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, filterByAssetClasses, + filterByDataSource, + filterBySymbol, filterByTags }); @@ -310,17 +315,22 @@ export class PortfolioController { @Get('dividends') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + @UseInterceptors(TransformDataSourceInRequestInterceptor) public async getDividends( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Query('accounts') filterByAccounts?: string, @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, @Query('groupBy') groupBy?: GroupBy, @Query('range') dateRange: DateRange = 'max', + @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string ): Promise { const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, filterByAssetClasses, + filterByDataSource, + filterBySymbol, filterByTags }); @@ -377,21 +387,26 @@ export class PortfolioController { @Get('holdings') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(RedactValuesInResponseInterceptor) + @UseInterceptors(TransformDataSourceInRequestInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor) public async getHoldings( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Query('accounts') filterByAccounts?: string, @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, @Query('holdingType') filterByHoldingType?: string, @Query('query') filterBySearchQuery?: string, @Query('range') dateRange: DateRange = 'max', + @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string ): Promise { const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, filterByAssetClasses, + filterByDataSource, filterByHoldingType, filterBySearchQuery, + filterBySymbol, filterByTags }); @@ -407,17 +422,22 @@ export class PortfolioController { @Get('investments') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) + @UseInterceptors(TransformDataSourceInRequestInterceptor) public async getInvestments( @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Query('accounts') filterByAccounts?: string, @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, @Query('groupBy') groupBy?: GroupBy, @Query('range') dateRange: DateRange = 'max', + @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string ): Promise { const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, filterByAssetClasses, + filterByDataSource, + filterBySymbol, filterByTags }); @@ -472,6 +492,7 @@ export class PortfolioController { @Get('performance') @UseGuards(AuthGuard('jwt'), HasPermissionGuard) @UseInterceptors(PerformanceLoggingInterceptor) + @UseInterceptors(TransformDataSourceInRequestInterceptor) @UseInterceptors(TransformDataSourceInResponseInterceptor) @Version('2') @LogPerformance @@ -479,7 +500,9 @@ export class PortfolioController { @Headers(HEADER_KEY_IMPERSONATION.toLowerCase()) impersonationId: string, @Query('accounts') filterByAccounts?: string, @Query('assetClasses') filterByAssetClasses?: string, + @Query('dataSource') filterByDataSource?: string, @Query('range') dateRange: DateRange = 'max', + @Query('symbol') filterBySymbol?: string, @Query('tags') filterByTags?: string, @Query('withExcludedAccounts') withExcludedAccounts = false, @Query('timeWeightedPerformance') calculateTimeWeightedPerformance = false @@ -487,6 +510,8 @@ export class PortfolioController { const filters = this.apiService.buildFiltersFromQueryParams({ filterByAccounts, filterByAssetClasses, + filterByDataSource, + filterBySymbol, filterByTags }); diff --git a/apps/api/src/app/portfolio/portfolio.service.ts b/apps/api/src/app/portfolio/portfolio.service.ts index 5827d63b6..bae845fab 100644 --- a/apps/api/src/app/portfolio/portfolio.service.ts +++ b/apps/api/src/app/portfolio/portfolio.service.ts @@ -420,15 +420,16 @@ export class PortfolioService { ); } - const dataGatheringItems = positions.map(({ dataSource, symbol }) => { + const assetProfileIdentifiers = positions.map(({ dataSource, symbol }) => { return { dataSource, symbol }; }); - const symbolProfiles = - await this.symbolProfileService.getSymbolProfiles(dataGatheringItems); + const symbolProfiles = await this.symbolProfileService.getSymbolProfiles( + assetProfileIdentifiers + ); const symbolProfileMap: { [symbol: string]: EnhancedSymbolProfile } = {}; for (const symbolProfile of symbolProfiles) { @@ -868,7 +869,7 @@ export class PortfolioService { if (isEmpty(historicalData)) { try { historicalData = await this.dataProviderService.getHistoricalRaw({ - dataGatheringItems: [ + assetProfileIdentifiers: [ { dataSource: DataSource.YAHOO, symbol: aSymbol } ], from: portfolioStart, @@ -975,7 +976,7 @@ export class PortfolioService { return !quantity.eq(0); }); - const dataGatheringItems = positions.map(({ dataSource, symbol }) => { + const assetProfileIdentifiers = positions.map(({ dataSource, symbol }) => { return { dataSource, symbol @@ -983,7 +984,10 @@ export class PortfolioService { }); const [dataProviderResponses, symbolProfiles] = await Promise.all([ - this.dataProviderService.getQuotes({ user, items: dataGatheringItems }), + this.dataProviderService.getQuotes({ + user, + items: assetProfileIdentifiers + }), this.symbolProfileService.getSymbolProfiles( positions.map(({ dataSource, symbol }) => { return { dataSource, symbol }; diff --git a/apps/api/src/app/subscription/subscription.service.ts b/apps/api/src/app/subscription/subscription.service.ts index 7c1df023c..ae0260d8c 100644 --- a/apps/api/src/app/subscription/subscription.service.ts +++ b/apps/api/src/app/subscription/subscription.service.ts @@ -1,8 +1,16 @@ import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; -import { DEFAULT_LANGUAGE_CODE } from '@ghostfolio/common/config'; +import { PropertyService } from '@ghostfolio/api/services/property/property.service'; +import { + DEFAULT_LANGUAGE_CODE, + PROPERTY_STRIPE_CONFIG +} from '@ghostfolio/common/config'; import { parseDate } from '@ghostfolio/common/helper'; -import { SubscriptionOffer, UserWithSettings } from '@ghostfolio/common/types'; +import { SubscriptionOffer } from '@ghostfolio/common/interfaces'; +import { + SubscriptionOfferKey, + UserWithSettings +} from '@ghostfolio/common/types'; import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type'; import { Injectable, Logger } from '@nestjs/common'; @@ -17,14 +25,17 @@ export class SubscriptionService { public constructor( private readonly configurationService: ConfigurationService, - private readonly prismaService: PrismaService + private readonly prismaService: PrismaService, + private readonly propertyService: PropertyService ) { - this.stripe = new Stripe( - this.configurationService.get('STRIPE_SECRET_KEY'), - { - apiVersion: '2024-04-10' - } - ); + if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { + this.stripe = new Stripe( + this.configurationService.get('STRIPE_SECRET_KEY'), + { + apiVersion: '2024-09-30.acacia' + } + ); + } } public async createCheckoutSession({ @@ -36,6 +47,18 @@ export class SubscriptionService { priceId: string; user: UserWithSettings; }) { + const subscriptionOffers: { + [offer in SubscriptionOfferKey]: SubscriptionOffer; + } = + ((await this.propertyService.getByKey(PROPERTY_STRIPE_CONFIG)) as any) ?? + {}; + + const subscriptionOffer = Object.values(subscriptionOffers).find( + (subscriptionOffer) => { + return subscriptionOffer.priceId === priceId; + } + ); + const checkoutSessionCreateParams: Stripe.Checkout.SessionCreateParams = { cancel_url: `${this.configurationService.get('ROOT_URL')}/${ user.Settings?.settings?.language ?? DEFAULT_LANGUAGE_CODE @@ -47,6 +70,13 @@ export class SubscriptionService { quantity: 1 } ], + locale: + (user.Settings?.settings + ?.language as Stripe.Checkout.SessionCreateParams.Locale) ?? + DEFAULT_LANGUAGE_CODE, + metadata: subscriptionOffer + ? { subscriptionOffer: JSON.stringify(subscriptionOffer) } + : {}, mode: 'payment', payment_method_types: ['card'], success_url: `${this.configurationService.get( @@ -73,17 +103,25 @@ export class SubscriptionService { public async createSubscription({ duration = '1 year', + durationExtension, price, userId }: { duration?: StringValue; + durationExtension?: StringValue; price: number; userId: string; }) { + let expiresAt = addMilliseconds(new Date(), ms(duration)); + + if (durationExtension) { + expiresAt = addMilliseconds(expiresAt, ms(durationExtension)); + } + await this.prismaService.subscription.create({ data: { + expiresAt, price, - expiresAt: addMilliseconds(new Date(), ms(duration)), User: { connect: { id: userId @@ -95,10 +133,21 @@ export class SubscriptionService { public async createSubscriptionViaStripe(aCheckoutSessionId: string) { try { + let durationExtension: StringValue; + const session = await this.stripe.checkout.sessions.retrieve(aCheckoutSessionId); + const subscriptionOffer: SubscriptionOffer = JSON.parse( + session.metadata.subscriptionOffer ?? '{}' + ); + + if (subscriptionOffer) { + durationExtension = subscriptionOffer.durationExtension; + } + await this.createSubscription({ + durationExtension, price: session.amount_total / 100, userId: session.client_reference_id }); @@ -121,7 +170,7 @@ export class SubscriptionService { return new Date(a.expiresAt) > new Date(b.expiresAt) ? a : b; }); - let offer: SubscriptionOffer = price ? 'renewal' : 'default'; + let offer: SubscriptionOfferKey = price ? 'renewal' : 'default'; if (isBefore(createdAt, parseDate('2023-01-01'))) { offer = 'renewal-early-bird-2023'; diff --git a/apps/api/src/app/symbol/symbol.controller.ts b/apps/api/src/app/symbol/symbol.controller.ts index b3b9dc109..89cdd416c 100644 --- a/apps/api/src/app/symbol/symbol.controller.ts +++ b/apps/api/src/app/symbol/symbol.controller.ts @@ -2,6 +2,7 @@ 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 { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces'; +import { LookupResponse } from '@ghostfolio/common/interfaces'; import type { RequestWithUser } from '@ghostfolio/common/types'; import { @@ -21,7 +22,6 @@ import { parseISO } from 'date-fns'; import { StatusCodes, getReasonPhrase } from 'http-status-codes'; import { isDate, isEmpty } from 'lodash'; -import { LookupItem } from './interfaces/lookup-item.interface'; import { SymbolItem } from './interfaces/symbol-item.interface'; import { SymbolService } from './symbol.service'; @@ -41,7 +41,7 @@ export class SymbolController { public async lookupSymbol( @Query('includeIndices') includeIndicesParam = 'false', @Query('query') query = '' - ): Promise<{ items: LookupItem[] }> { + ): Promise { const includeIndices = includeIndicesParam === 'true'; try { diff --git a/apps/api/src/app/symbol/symbol.service.ts b/apps/api/src/app/symbol/symbol.service.ts index 2baca18dd..56befb9b6 100644 --- a/apps/api/src/app/symbol/symbol.service.ts +++ b/apps/api/src/app/symbol/symbol.service.ts @@ -5,13 +5,15 @@ import { } 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 { HistoricalDataItem } from '@ghostfolio/common/interfaces'; +import { + HistoricalDataItem, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { UserWithSettings } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; import { format, subDays } from 'date-fns'; -import { LookupItem } from './interfaces/lookup-item.interface'; import { SymbolItem } from './interfaces/symbol-item.interface'; @Injectable() @@ -84,7 +86,7 @@ export class SymbolService { try { historicalData = await this.dataProviderService.getHistoricalRaw({ - dataGatheringItems: [{ dataSource, symbol }], + assetProfileIdentifiers: [{ dataSource, symbol }], from: date, to: date }); @@ -104,8 +106,8 @@ export class SymbolService { includeIndices?: boolean; query: string; user: UserWithSettings; - }): Promise<{ items: LookupItem[] }> { - const results: { items: LookupItem[] } = { items: [] }; + }): Promise { + const results: LookupResponse = { items: [] }; if (!query) { return results; diff --git a/apps/api/src/app/user/update-user-setting.dto.ts b/apps/api/src/app/user/update-user-setting.dto.ts index 317b4d689..d8e06790b 100644 --- a/apps/api/src/app/user/update-user-setting.dto.ts +++ b/apps/api/src/app/user/update-user-setting.dto.ts @@ -67,6 +67,14 @@ export class UpdateUserSettingDto { @IsOptional() 'filters.assetClasses'?: string[]; + @IsString() + @IsOptional() + 'filters.dataSource'?: string; + + @IsString() + @IsOptional() + 'filters.symbol'?: string; + @IsArray() @IsOptional() 'filters.tags'?: string[]; diff --git a/apps/api/src/app/user/user.service.ts b/apps/api/src/app/user/user.service.ts index 288e2aba2..443a2a052 100644 --- a/apps/api/src/app/user/user.service.ts +++ b/apps/api/src/app/user/user.service.ts @@ -37,7 +37,7 @@ import { UserWithSettings } from '@ghostfolio/common/types'; import { Injectable } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { Prisma, Role, User } from '@prisma/client'; -import { differenceInDays } from 'date-fns'; +import { differenceInDays, subDays } from 'date-fns'; import { sortBy, without } from 'lodash'; const crypto = require('crypto'); @@ -60,6 +60,13 @@ export class UserService { return this.prismaService.user.count(args); } + public createAccessToken(password: string, salt: string): string { + const hash = crypto.createHmac('sha512', salt); + hash.update(password); + + return hash.digest('hex'); + } + public async getUser( { Account, id, permissions, Settings, subscription }: UserWithSettings, aLocale = locale @@ -358,13 +365,6 @@ export class UserService { }); } - public createAccessToken(password: string, salt: string): string { - const hash = crypto.createHmac('sha512', salt); - hash.update(password); - - return hash.digest('hex'); - } - public async createUser({ data }: { @@ -426,17 +426,6 @@ export class UserService { return user; } - public async updateUser(params: { - where: Prisma.UserWhereUniqueInput; - data: Prisma.UserUpdateInput; - }): Promise { - const { where, data } = params; - return this.prismaService.user.update({ - data, - where - }); - } - public async deleteUser(where: Prisma.UserWhereUniqueInput): Promise { try { await this.prismaService.access.deleteMany({ @@ -473,6 +462,32 @@ export class UserService { }); } + public async resetAnalytics() { + return this.prismaService.analytics.updateMany({ + data: { + dataProviderGhostfolioDailyRequests: 0 + }, + where: { + updatedAt: { + gte: subDays(new Date(), 1) + } + } + }); + } + + public async updateUser({ + data, + where + }: { + data: Prisma.UserUpdateInput; + where: Prisma.UserWhereUniqueInput; + }): Promise { + return this.prismaService.user.update({ + data, + where + }); + } + public async updateUserSetting({ emitPortfolioChangedEvent, userId, diff --git a/apps/api/src/services/cron.service.ts b/apps/api/src/services/cron.service.ts index 17e970c1b..7a1b30b5f 100644 --- a/apps/api/src/services/cron.service.ts +++ b/apps/api/src/services/cron.service.ts @@ -1,3 +1,4 @@ +import { UserService } from '@ghostfolio/api/app/user/user.service'; import { DATA_GATHERING_QUEUE_PRIORITY_LOW, GATHER_ASSET_PROFILE_PROCESS, @@ -9,6 +10,7 @@ import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; import { Injectable } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; +import { ConfigurationService } from './configuration/configuration.service'; import { ExchangeRateDataService } from './exchange-rate-data/exchange-rate-data.service'; import { PropertyService } from './property/property.service'; import { DataGatheringService } from './queues/data-gathering/data-gathering.service'; @@ -19,10 +21,12 @@ export class CronService { private static readonly EVERY_SUNDAY_AT_LUNCH_TIME = '0 12 * * 0'; public constructor( + private readonly configurationService: ConfigurationService, private readonly dataGatheringService: DataGatheringService, private readonly exchangeRateDataService: ExchangeRateDataService, private readonly propertyService: PropertyService, - private readonly twitterBotService: TwitterBotService + private readonly twitterBotService: TwitterBotService, + private readonly userService: UserService ) {} @Cron(CronExpression.EVERY_HOUR) @@ -42,6 +46,13 @@ export class CronService { this.twitterBotService.tweetFearAndGreedIndex(); } + @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) + public async runEveryDayAtMidnight() { + if (this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) { + this.userService.resetAnalytics(); + } + } + @Cron(CronService.EVERY_SUNDAY_AT_LUNCH_TIME) public async runEverySundayAtTwelvePm() { if (await this.isDataGatheringEnabled()) { diff --git a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts index 016584949..5c9eee127 100644 --- a/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts +++ b/apps/api/src/services/data-provider/alpha-vantage/alpha-vantage.service.ts @@ -1,4 +1,3 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, @@ -12,7 +11,10 @@ import { IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { Injectable } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; @@ -119,9 +121,7 @@ export class AlphaVantageService implements DataProviderInterface { return undefined; } - public async search({ - query - }: GetSearchParams): Promise<{ items: LookupItem[] }> { + public async search({ query }: GetSearchParams): Promise { const result = await this.alphaVantage.data.search(query); return { diff --git a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts index d420c51fd..7d6f22c60 100644 --- a/apps/api/src/services/data-provider/coingecko/coingecko.service.ts +++ b/apps/api/src/services/data-provider/coingecko/coingecko.service.ts @@ -1,4 +1,3 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, @@ -13,7 +12,11 @@ import { } from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + LookupItem, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { @@ -221,9 +224,7 @@ export class CoinGeckoService implements DataProviderInterface { return 'bitcoin'; } - public async search({ - query - }: GetSearchParams): Promise<{ items: LookupItem[] }> { + public async search({ query }: GetSearchParams): Promise { let items: LookupItem[] = []; try { 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 9853b0186..2c7b2e08e 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,4 @@ import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { LogPerformance } from '@ghostfolio/api/interceptors/performance-logging/performance-logging.interceptor'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface } from '@ghostfolio/api/services/data-provider/interfaces/data-provider.interface'; @@ -21,7 +20,11 @@ import { getStartOfUtcDate, isDerivedCurrency } from '@ghostfolio/common/helper'; -import { AssetProfileIdentifier } from '@ghostfolio/common/interfaces'; +import { + AssetProfileIdentifier, + LookupItem, + LookupResponse +} from '@ghostfolio/common/interfaces'; import type { Granularity, UserWithSettings } from '@ghostfolio/common/types'; import { Inject, Injectable, Logger } from '@nestjs/common'; @@ -89,11 +92,11 @@ export class DataProviderService { const promises = []; - for (const [dataSource, dataGatheringItems] of Object.entries( + for (const [dataSource, assetProfileIdentifiers] of Object.entries( itemsGroupedByDataSource )) { - const symbols = dataGatheringItems.map((dataGatheringItem) => { - return dataGatheringItem.symbol; + const symbols = assetProfileIdentifiers.map(({ symbol }) => { + return symbol; }); for (const symbol of symbols) { @@ -240,11 +243,11 @@ export class DataProviderService { } public async getHistoricalRaw({ - dataGatheringItems, + assetProfileIdentifiers, from, to }: { - dataGatheringItems: AssetProfileIdentifier[]; + assetProfileIdentifiers: AssetProfileIdentifier[]; from: Date; to: Date; }): Promise<{ @@ -253,25 +256,32 @@ export class DataProviderService { for (const { currency, rootCurrency } of DERIVED_CURRENCIES) { if ( this.hasCurrency({ - dataGatheringItems, + assetProfileIdentifiers, currency: `${DEFAULT_CURRENCY}${currency}` }) ) { // Skip derived currency - dataGatheringItems = dataGatheringItems.filter(({ symbol }) => { - return symbol !== `${DEFAULT_CURRENCY}${currency}`; - }); + assetProfileIdentifiers = assetProfileIdentifiers.filter( + ({ symbol }) => { + return symbol !== `${DEFAULT_CURRENCY}${currency}`; + } + ); // Add root currency - dataGatheringItems.push({ + assetProfileIdentifiers.push({ dataSource: this.getDataSourceForExchangeRates(), symbol: `${DEFAULT_CURRENCY}${rootCurrency}` }); } } - dataGatheringItems = uniqWith(dataGatheringItems, (obj1, obj2) => { - return obj1.dataSource === obj2.dataSource && obj1.symbol === obj2.symbol; - }); + assetProfileIdentifiers = uniqWith( + assetProfileIdentifiers, + (obj1, obj2) => { + return ( + obj1.dataSource === obj2.dataSource && obj1.symbol === obj2.symbol + ); + } + ); const result: { [symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; @@ -281,7 +291,7 @@ export class DataProviderService { data: { [date: string]: IDataProviderHistoricalResponse }; symbol: string; }>[] = []; - for (const { dataSource, symbol } of dataGatheringItems) { + for (const { dataSource, symbol } of assetProfileIdentifiers) { const dataProvider = this.getDataProvider(dataSource); if (dataProvider.canHandle(symbol)) { if (symbol === `${DEFAULT_CURRENCY}USX`) { @@ -417,7 +427,7 @@ export class DataProviderService { const promises: Promise[] = []; - for (const [dataSource, dataGatheringItems] of Object.entries( + for (const [dataSource, assetProfileIdentifiers] of Object.entries( itemsGroupedByDataSource )) { const dataProvider = this.getDataProvider(DataSource[dataSource]); @@ -430,7 +440,7 @@ export class DataProviderService { continue; } - const symbols = dataGatheringItems + const symbols = assetProfileIdentifiers .filter(({ symbol }) => { return !isDerivedCurrency(getCurrencyFromSymbol(symbol)); }) @@ -594,9 +604,9 @@ export class DataProviderService { includeIndices?: boolean; query: string; user: UserWithSettings; - }): Promise<{ items: LookupItem[] }> { - const promises: Promise<{ items: LookupItem[] }>[] = []; + }): Promise { let lookupItems: LookupItem[] = []; + const promises: Promise[] = []; if (query?.length < 2) { return { items: lookupItems }; @@ -626,9 +636,9 @@ export class DataProviderService { }); const filteredItems = lookupItems - .filter((lookupItem) => { + .filter(({ currency }) => { // Only allow symbols with supported currency - return lookupItem.currency ? true : false; + return currency ? true : false; }) .sort(({ name: name1 }, { name: name2 }) => { return name1?.toLowerCase().localeCompare(name2?.toLowerCase()); @@ -654,13 +664,13 @@ export class DataProviderService { } private hasCurrency({ - currency, - dataGatheringItems + assetProfileIdentifiers, + currency }: { + assetProfileIdentifiers: AssetProfileIdentifier[]; currency: string; - dataGatheringItems: AssetProfileIdentifier[]; }) { - return dataGatheringItems.some(({ dataSource, symbol }) => { + return assetProfileIdentifiers.some(({ dataSource, symbol }) => { return ( dataSource === this.getDataSourceForExchangeRates() && symbol === currency diff --git a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts index c3c948b47..7329b821b 100644 --- a/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts +++ b/apps/api/src/services/data-provider/eod-historical-data/eod-historical-data.service.ts @@ -1,4 +1,3 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, @@ -17,7 +16,11 @@ import { REPLACE_NAME_PARTS } from '@ghostfolio/common/config'; import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper'; -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + LookupItem, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { MarketState } from '@ghostfolio/common/types'; import { Injectable, Logger } from '@nestjs/common'; @@ -317,9 +320,7 @@ export class EodHistoricalDataService implements DataProviderInterface { return 'AAPL.US'; } - public async search({ - query - }: GetSearchParams): Promise<{ items: LookupItem[] }> { + public async search({ query }: GetSearchParams): Promise { const searchResult = await this.getSearchResult(query); return { 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 7d5b38479..9334fc4cd 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 @@ -1,4 +1,3 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, @@ -13,7 +12,11 @@ import { } from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + LookupItem, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; @@ -169,9 +172,7 @@ export class FinancialModelingPrepService implements DataProviderInterface { return 'AAPL'; } - public async search({ - query - }: GetSearchParams): Promise<{ items: LookupItem[] }> { + public async search({ query }: GetSearchParams): Promise { let items: LookupItem[] = []; try { diff --git a/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts b/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts index 9f2344233..f18d670d1 100644 --- a/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts +++ b/apps/api/src/services/data-provider/google-sheets/google-sheets.service.ts @@ -1,4 +1,3 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, @@ -14,7 +13,10 @@ import { import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { SymbolProfileService } from '@ghostfolio/api/services/symbol-profile/symbol-profile.service'; import { DATE_FORMAT, parseDate } from '@ghostfolio/common/helper'; -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; @@ -157,9 +159,7 @@ export class GoogleSheetsService implements DataProviderInterface { return 'INDEXSP:.INX'; } - public async search({ - query - }: GetSearchParams): Promise<{ items: LookupItem[] }> { + public async search({ query }: GetSearchParams): Promise { const items = await this.prismaService.symbolProfile.findMany({ select: { assetClass: true, 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 3b3644473..7352ce78a 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 @@ -1,9 +1,11 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { IDataProviderHistoricalResponse, IDataProviderResponse } from '@ghostfolio/api/services/interfaces/interfaces'; -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { Granularity } from '@ghostfolio/common/types'; import { DataSource, SymbolProfile } from '@prisma/client'; @@ -44,10 +46,7 @@ export interface DataProviderInterface { getTestSymbol(): string; - search({ - includeIndices, - query - }: GetSearchParams): Promise<{ items: LookupItem[] }>; + search({ includeIndices, query }: GetSearchParams): Promise; } export interface GetDividendsParams { 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 b202f42d2..8d6e6012c 100644 --- a/apps/api/src/services/data-provider/manual/manual.service.ts +++ b/apps/api/src/services/data-provider/manual/manual.service.ts @@ -1,4 +1,3 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, @@ -21,6 +20,7 @@ import { } from '@ghostfolio/common/helper'; import { DataProviderInfo, + LookupResponse, ScraperConfiguration } from '@ghostfolio/common/interfaces'; @@ -227,9 +227,7 @@ export class ManualService implements DataProviderInterface { return undefined; } - public async search({ - query - }: GetSearchParams): Promise<{ items: LookupItem[] }> { + public async search({ query }: GetSearchParams): Promise { let items = await this.prismaService.symbolProfile.findMany({ select: { assetClass: true, 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 e47e96d88..29e7f4ee9 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 @@ -1,4 +1,3 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; import { DataProviderInterface, @@ -13,7 +12,10 @@ import { } from '@ghostfolio/api/services/interfaces/interfaces'; import { ghostfolioFearAndGreedIndexSymbol } from '@ghostfolio/common/config'; import { DATE_FORMAT, getYesterday } from '@ghostfolio/common/helper'; -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; @@ -121,7 +123,7 @@ export class RapidApiService implements DataProviderInterface { return undefined; } - public async search({}: GetSearchParams): Promise<{ items: LookupItem[] }> { + public async search({}: GetSearchParams): Promise { return { items: [] }; } diff --git a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts index 2d67c646c..27da18ab0 100644 --- a/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts +++ b/apps/api/src/services/data-provider/yahoo-finance/yahoo-finance.service.ts @@ -1,4 +1,3 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service'; import { YahooFinanceDataEnhancerService } from '@ghostfolio/api/services/data-provider/data-enhancer/yahoo-finance/yahoo-finance.service'; import { @@ -14,7 +13,11 @@ import { } from '@ghostfolio/api/services/interfaces/interfaces'; import { DEFAULT_CURRENCY } from '@ghostfolio/common/config'; import { DATE_FORMAT } from '@ghostfolio/common/helper'; -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; +import { + DataProviderInfo, + LookupItem, + LookupResponse +} from '@ghostfolio/common/interfaces'; import { Injectable, Logger } from '@nestjs/common'; import { DataSource, SymbolProfile } from '@prisma/client'; @@ -224,7 +227,7 @@ export class YahooFinanceService implements DataProviderInterface { public async search({ includeIndices = false, query - }: GetSearchParams): Promise<{ items: LookupItem[] }> { + }: GetSearchParams): Promise { const items: LookupItem[] = []; try { diff --git a/apps/api/src/services/prisma/prisma.module.ts b/apps/api/src/services/prisma/prisma.module.ts index 3875c8cab..24da61047 100644 --- a/apps/api/src/services/prisma/prisma.module.ts +++ b/apps/api/src/services/prisma/prisma.module.ts @@ -1,9 +1,10 @@ import { PrismaService } from '@ghostfolio/api/services/prisma/prisma.service'; import { Module } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; @Module({ - providers: [PrismaService], - exports: [PrismaService] + exports: [PrismaService], + providers: [ConfigService, PrismaService] }) export class PrismaModule {} diff --git a/apps/api/src/services/prisma/prisma.service.ts b/apps/api/src/services/prisma/prisma.service.ts index e99d6ecf9..4673cbd19 100644 --- a/apps/api/src/services/prisma/prisma.service.ts +++ b/apps/api/src/services/prisma/prisma.service.ts @@ -1,16 +1,38 @@ import { Injectable, Logger, + LogLevel, OnModuleDestroy, OnModuleInit } from '@nestjs/common'; -import { PrismaClient } from '@prisma/client'; +import { ConfigService } from '@nestjs/config'; +import { Prisma, PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { + public constructor(configService: ConfigService) { + let customLogLevels: LogLevel[]; + + try { + customLogLevels = JSON.parse( + configService.get('LOG_LEVELS') + ) as LogLevel[]; + } catch {} + + const log: Prisma.LogDefinition[] = + customLogLevels?.includes('debug') || customLogLevels?.includes('verbose') + ? [{ emit: 'stdout', level: 'query' }] + : []; + + super({ + log, + errorFormat: 'colorless' + }); + } + public async onModuleInit() { try { await this.$connect(); 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 88f3baaf9..1f88375d5 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 @@ -96,7 +96,7 @@ export class DataGatheringProcessor { ); const historicalData = await this.dataProviderService.getHistoricalRaw({ - dataGatheringItems: [{ dataSource, symbol }], + assetProfileIdentifiers: [{ dataSource, symbol }], from: currentDate, to: new Date() }); @@ -214,7 +214,7 @@ export class DataGatheringProcessor { dates = dates.filter((d) => !marketData.some((md) => isEqual(md, d))); const historicalData = await this.dataProviderService.getHistoricalRaw({ - dataGatheringItems: [{ dataSource, symbol }], + assetProfileIdentifiers: [{ dataSource, symbol }], from: firstEntry.date, to: new Date() }); 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 24b174785..45b429d48 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 @@ -142,7 +142,7 @@ export class DataGatheringService { }) { try { const historicalData = await this.dataProviderService.getHistoricalRaw({ - dataGatheringItems: [{ dataSource, symbol }], + assetProfileIdentifiers: [{ dataSource, symbol }], from: date, to: date }); diff --git a/apps/client/src/app/app.component.html b/apps/client/src/app/app.component.html index b12855488..7560e15e5 100644 --- a/apps/client/src/app/app.component.html +++ b/apps/client/src/app/app.component.html @@ -33,6 +33,7 @@ [deviceType]="deviceType" [hasPermissionToChangeDateRange]="hasPermissionToChangeDateRange" [hasPermissionToChangeFilters]="hasPermissionToChangeFilters" + [hasPromotion]="hasPromotion" [hasTabs]="hasTabs" [info]="info" [pageTitle]="pageTitle" diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts index 75841686c..86d4282a2 100644 --- a/apps/client/src/app/app.component.ts +++ b/apps/client/src/app/app.component.ts @@ -57,6 +57,7 @@ export class AppComponent implements OnDestroy, OnInit { public hasPermissionToAccessFearAndGreedIndex: boolean; public hasPermissionToChangeDateRange: boolean; public hasPermissionToChangeFilters: boolean; + public hasPromotion = false; public hasTabs = false; public info: InfoItem; public pageTitle: string; @@ -136,6 +137,10 @@ export class AppComponent implements OnDestroy, OnInit { permissions.enableFearAndGreedIndex ); + this.hasPromotion = + !!this.info?.subscriptionOffers?.default?.coupon || + !!this.info?.subscriptionOffers?.default?.durationExtension; + this.impersonationStorageService .onChangeHasImpersonation() .pipe(takeUntil(this.unsubscribeSubject)) @@ -231,6 +236,14 @@ export class AppComponent implements OnDestroy, OnInit { this.hasInfoMessage = this.canCreateAccount || !!this.user?.systemMessage; + this.hasPromotion = + !!this.info?.subscriptionOffers?.[ + this.user?.subscription?.offer ?? 'default' + ]?.coupon || + !!this.info?.subscriptionOffers?.[ + this.user?.subscription?.offer ?? 'default' + ]?.durationExtension; + this.initializeTheme(this.user?.settings.colorScheme); this.changeDetectorRef.markForCheck(); diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html index ff36c2ebe..8a611d935 100644 --- a/apps/client/src/app/components/header/header.component.html +++ b/apps/client/src/app/components/header/header.component.html @@ -88,15 +88,20 @@
  • Pricing + + Pricing + @if (currentRoute !== routePricing && hasPromotion) { + % + } + +
  • }
  • @@ -290,12 +295,17 @@ ) { Pricing + + Pricing + @if (currentRoute !== routePricing && hasPromotion) { + % + } + + } Pricing + + Pricing + @if (currentRoute !== routePricing && hasPromotion) { + % + } + +
  • } @if (hasPermissionToAccessFearAndGreedIndex) { diff --git a/apps/client/src/app/components/header/header.component.ts b/apps/client/src/app/components/header/header.component.ts index af69f7b39..004fa5f3f 100644 --- a/apps/client/src/app/components/header/header.component.ts +++ b/apps/client/src/app/components/header/header.component.ts @@ -58,6 +58,7 @@ export class HeaderComponent implements OnChanges { @Input() deviceType: string; @Input() hasPermissionToChangeDateRange: boolean; @Input() hasPermissionToChangeFilters: boolean; + @Input() hasPromotion: boolean; @Input() hasTabs: boolean; @Input() info: InfoItem; @Input() pageTitle: string; @@ -174,24 +175,18 @@ export class HeaderComponent implements OnChanges { const userSetting: UpdateUserSettingDto = {}; for (const filter of filters) { - const filtersType = this.getFilterType(filter.type); - - const userFilters = filters - .filter((f) => f.type === filter.type && filter.id) - .map((f) => f.id); - - userSetting[`filters.${filtersType}`] = userFilters.length - ? userFilters - : null; + if (filter.type === 'ACCOUNT') { + userSetting['filters.accounts'] = filter.id ? [filter.id] : null; + } else if (filter.type === 'ASSET_CLASS') { + userSetting['filters.assetClasses'] = filter.id ? [filter.id] : null; + } else if (filter.type === 'DATA_SOURCE') { + userSetting['filters.dataSource'] = filter.id ? filter.id : null; + } else if (filter.type === 'SYMBOL') { + userSetting['filters.symbol'] = filter.id ? filter.id : null; + } else if (filter.type === 'TAG') { + userSetting['filters.tags'] = filter.id ? [filter.id] : null; + } } - ['ACCOUNT', 'ASSET_CLASS', 'TAG'] - .filter( - (fitlerType) => - !filters.some((f: Filter) => f.type.toString() === fitlerType) - ) - .forEach((filterType) => { - userSetting[`filters.${this.getFilterType(filterType)}`] = null; - }); this.dataService .putUserSetting(userSetting) @@ -285,13 +280,4 @@ export class HeaderComponent implements OnChanges { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } - private getFilterType(filterType: string) { - if (filterType === 'ACCOUNT') { - return 'accounts'; - } else if (filterType === 'ASSET_CLASS') { - return 'assetClasses'; - } else if (filterType === 'TAG') { - return 'tags'; - } - } } diff --git a/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts b/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts index 93bbe641c..bde555d8e 100644 --- a/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts +++ b/apps/client/src/app/components/user-account-membership/user-account-membership.component.ts @@ -16,6 +16,7 @@ import { MatSnackBarRef, TextOnlySnackBar } from '@angular/material/snack-bar'; +import { StringValue } from 'ms'; import { StripeService } from 'ngx-stripe'; import { EMPTY, Subject } from 'rxjs'; import { catchError, switchMap, takeUntil } from 'rxjs/operators'; @@ -31,6 +32,7 @@ export class UserAccountMembershipComponent implements OnDestroy { public coupon: number; public couponId: string; public defaultDateFormat: string; + public durationExtension: StringValue; public hasPermissionForSubscription: boolean; public hasPermissionToUpdateUserSettings: boolean; public price: number; @@ -51,7 +53,7 @@ export class UserAccountMembershipComponent implements OnDestroy { private stripeService: StripeService, private userService: UserService ) { - const { baseCurrency, globalPermissions, subscriptions } = + const { baseCurrency, globalPermissions, subscriptionOffers } = this.dataService.fetchInfo(); this.baseCurrency = baseCurrency; @@ -76,11 +78,18 @@ export class UserAccountMembershipComponent implements OnDestroy { permissions.updateUserSettings ); - this.coupon = subscriptions?.[this.user.subscription.offer]?.coupon; + this.coupon = + subscriptionOffers?.[this.user.subscription.offer]?.coupon; this.couponId = - subscriptions?.[this.user.subscription.offer]?.couponId; - this.price = subscriptions?.[this.user.subscription.offer]?.price; - this.priceId = subscriptions?.[this.user.subscription.offer]?.priceId; + subscriptionOffers?.[this.user.subscription.offer]?.couponId; + this.durationExtension = + subscriptionOffers?.[ + this.user.subscription.offer + ]?.durationExtension; + this.price = + subscriptionOffers?.[this.user.subscription.offer]?.price; + this.priceId = + subscriptionOffers?.[this.user.subscription.offer]?.priceId; this.changeDetectorRef.markForCheck(); } diff --git a/apps/client/src/app/components/user-account-membership/user-account-membership.html b/apps/client/src/app/components/user-account-membership/user-account-membership.html index d30ce7bdd..82b329a64 100644 --- a/apps/client/src/app/components/user-account-membership/user-account-membership.html +++ b/apps/client/src/app/components/user-account-membership/user-account-membership.html @@ -34,6 +34,16 @@  per year } + @if (durationExtension) { +
    +
    + Limited Offer! Get + {{ durationExtension }} extra +
    +
    + } }
    @if (!user?.subscription?.expiresAt) { diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page-routing.module.ts b/apps/client/src/app/pages/portfolio/fire/fire-page-routing.module.ts index 885dc5509..96260adda 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page-routing.module.ts +++ b/apps/client/src/app/pages/portfolio/fire/fire-page-routing.module.ts @@ -10,7 +10,7 @@ const routes: Routes = [ canActivate: [AuthGuard], component: FirePageComponent, path: '', - title: $localize`FIRE` + title: 'FIRE' } ]; diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts index d20c66912..897b9824e 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.component.ts @@ -1,12 +1,7 @@ -import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto'; import { DataService } from '@ghostfolio/client/services/data.service'; import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; import { UserService } from '@ghostfolio/client/services/user/user.service'; -import { - PortfolioReport, - PortfolioReportRule, - User -} from '@ghostfolio/common/interfaces'; +import { User } from '@ghostfolio/common/interfaces'; import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; @@ -21,18 +16,11 @@ import { takeUntil } from 'rxjs/operators'; templateUrl: './fire-page.html' }) export class FirePageComponent implements OnDestroy, OnInit { - public accountClusterRiskRules: PortfolioReportRule[]; - public currencyClusterRiskRules: PortfolioReportRule[]; public deviceType: string; - public economicMarketClusterRiskRules: PortfolioReportRule[]; - public emergencyFundRules: PortfolioReportRule[]; - public feeRules: PortfolioReportRule[]; public fireWealth: Big; public hasImpersonationId: boolean; public hasPermissionToUpdateUserSettings: boolean; - public inactiveRules: PortfolioReportRule[]; public isLoading = false; - public isLoadingPortfolioReport = false; public user: User; public withdrawalRatePerMonth: Big; public withdrawalRatePerYear: Big; @@ -95,8 +83,6 @@ export class FirePageComponent implements OnDestroy, OnInit { this.changeDetectorRef.markForCheck(); } }); - - this.initializePortfolioReport(); } public onAnnualInterestRateChange(annualInterestRate: number) { @@ -133,21 +119,6 @@ export class FirePageComponent implements OnDestroy, OnInit { }); }); } - - public onRulesUpdated(event: UpdateUserSettingDto) { - this.dataService - .putUserSetting(event) - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(() => { - this.userService - .get(true) - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe(); - - this.initializePortfolioReport(); - }); - } - public onSavingsRateChange(savingsRate: number) { this.dataService .putUserSetting({ savingsRate }) @@ -187,66 +158,4 @@ export class FirePageComponent implements OnDestroy, OnInit { this.unsubscribeSubject.next(); this.unsubscribeSubject.complete(); } - - private initializePortfolioReport() { - this.isLoadingPortfolioReport = true; - - this.dataService - .fetchPortfolioReport() - .pipe(takeUntil(this.unsubscribeSubject)) - .subscribe((portfolioReport) => { - this.inactiveRules = this.mergeInactiveRules(portfolioReport); - - this.accountClusterRiskRules = - portfolioReport.rules['accountClusterRisk']?.filter( - ({ isActive }) => { - return isActive; - } - ) ?? null; - - this.currencyClusterRiskRules = - portfolioReport.rules['currencyClusterRisk']?.filter( - ({ isActive }) => { - return isActive; - } - ) ?? null; - - this.economicMarketClusterRiskRules = - portfolioReport.rules['economicMarketClusterRisk']?.filter( - ({ isActive }) => { - return isActive; - } - ) ?? null; - - this.emergencyFundRules = - portfolioReport.rules['emergencyFund']?.filter(({ isActive }) => { - return isActive; - }) ?? null; - - this.feeRules = - portfolioReport.rules['fees']?.filter(({ isActive }) => { - return isActive; - }) ?? null; - - this.isLoadingPortfolioReport = false; - - this.changeDetectorRef.markForCheck(); - }); - } - - private mergeInactiveRules(report: PortfolioReport): PortfolioReportRule[] { - let inactiveRules: PortfolioReportRule[] = []; - - for (const category in report.rules) { - const rulesArray = report.rules[category]; - - inactiveRules = inactiveRules.concat( - rulesArray.filter(({ isActive }) => { - return !isActive; - }) - ); - } - - return inactiveRules; - } } diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.html b/apps/client/src/app/pages/portfolio/fire/fire-page.html index 7a336b62f..77fd1640c 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.html +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.html @@ -101,133 +101,3 @@ }
    - -
    -
    -
    -

    X-ray

    -

    - Ghostfolio X-ray uses static analysis to identify potential issues - and risks in your portfolio. - It will be highly configurable in the future: activate / deactivate - rules and customize the thresholds to match your personal investment - style. -

    -
    -

    - Emergency Fund - @if (user?.subscription?.type === 'Basic') { - - } -

    - -
    -
    -

    - Currency Cluster Risks - @if (user?.subscription?.type === 'Basic') { - - } -

    - -
    -
    -

    - Account Cluster Risks - @if (user?.subscription?.type === 'Basic') { - - } -

    - -
    -
    -

    - Economic Market Cluster Risks - @if (user?.subscription?.type === 'Basic') { - - } -

    - -
    -
    -

    - Fees - @if (user?.subscription?.type === 'Basic') { - - } -

    - -
    - @if (inactiveRules?.length > 0) { -
    -

    Inactive

    - -
    - } -
    -
    -
    diff --git a/apps/client/src/app/pages/portfolio/fire/fire-page.module.ts b/apps/client/src/app/pages/portfolio/fire/fire-page.module.ts index 60e3127d9..a606ae1b4 100644 --- a/apps/client/src/app/pages/portfolio/fire/fire-page.module.ts +++ b/apps/client/src/app/pages/portfolio/fire/fire-page.module.ts @@ -1,4 +1,3 @@ -import { GfRulesModule } from '@ghostfolio/client/components/rules/rules.module'; import { GfFireCalculatorComponent } from '@ghostfolio/ui/fire-calculator'; import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; import { GfValueComponent } from '@ghostfolio/ui/value'; @@ -17,7 +16,6 @@ import { FirePageComponent } from './fire-page.component'; FirePageRoutingModule, GfFireCalculatorComponent, GfPremiumIndicatorComponent, - GfRulesModule, GfValueComponent, NgxSkeletonLoaderModule ], diff --git a/apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts b/apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts index 6146c573c..20de6f8fa 100644 --- a/apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts +++ b/apps/client/src/app/pages/portfolio/portfolio-page-routing.module.ts @@ -34,6 +34,11 @@ const routes: Routes = [ path: 'fire', loadChildren: () => import('./fire/fire-page.module').then((m) => m.FirePageModule) + }, + { + path: 'x-ray', + loadChildren: () => + import('./x-ray/x-ray-page.module').then((m) => m.XRayPageModule) } ], component: PortfolioPageComponent, diff --git a/apps/client/src/app/pages/portfolio/portfolio-page.component.ts b/apps/client/src/app/pages/portfolio/portfolio-page.component.ts index 0c980e25b..7f40bf1d7 100644 --- a/apps/client/src/app/pages/portfolio/portfolio-page.component.ts +++ b/apps/client/src/app/pages/portfolio/portfolio-page.component.ts @@ -46,8 +46,13 @@ export class PortfolioPageComponent implements OnDestroy, OnInit { }, { iconName: 'calculator-outline', - label: 'FIRE / X-ray', + label: 'FIRE ', path: ['/portfolio', 'fire'] + }, + { + iconName: 'scan-outline', + label: 'X-ray', + path: ['/portfolio', 'x-ray'] } ]; this.user = state.user; diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page-routing.module.ts b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page-routing.module.ts new file mode 100644 index 000000000..091cbc49f --- /dev/null +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page-routing.module.ts @@ -0,0 +1,21 @@ +import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; + +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { XRayPageComponent } from './x-ray-page.component'; + +const routes: Routes = [ + { + canActivate: [AuthGuard], + component: XRayPageComponent, + path: '', + title: 'X-ray' + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class XRayPageRoutingModule {} diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html new file mode 100644 index 000000000..cd03b49bb --- /dev/null +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.html @@ -0,0 +1,123 @@ +
    +
    +
    +

    X-ray

    +

    + Ghostfolio X-ray uses static analysis to uncover potential issues and + risks in your portfolio. Adjust the rules below and set custom + thresholds to align with your personal investment strategy. +

    +
    +

    + Emergency Fund + @if (user?.subscription?.type === 'Basic') { + + } +

    + +
    +
    +

    + Currency Cluster Risks + @if (user?.subscription?.type === 'Basic') { + + } +

    + +
    +
    +

    + Account Cluster Risks + @if (user?.subscription?.type === 'Basic') { + + } +

    + +
    +
    +

    + Economic Market Cluster Risks + @if (user?.subscription?.type === 'Basic') { + + } +

    + +
    +
    +

    + Fees + @if (user?.subscription?.type === 'Basic') { + + } +

    + +
    + @if (inactiveRules?.length > 0) { +
    +

    Inactive

    + +
    + } +
    +
    +
    diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.scss b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.scss new file mode 100644 index 000000000..5d4e87f30 --- /dev/null +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.scss @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts new file mode 100644 index 000000000..36f42fc3e --- /dev/null +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.component.ts @@ -0,0 +1,150 @@ +import { UpdateUserSettingDto } from '@ghostfolio/api/app/user/update-user-setting.dto'; +import { DataService } from '@ghostfolio/client/services/data.service'; +import { ImpersonationStorageService } from '@ghostfolio/client/services/impersonation-storage.service'; +import { UserService } from '@ghostfolio/client/services/user/user.service'; +import { + PortfolioReportRule, + PortfolioReport +} from '@ghostfolio/common/interfaces'; +import { User } from '@ghostfolio/common/interfaces/user.interface'; +import { hasPermission, permissions } from '@ghostfolio/common/permissions'; + +import { ChangeDetectorRef, Component } from '@angular/core'; +import { Subject, takeUntil } from 'rxjs'; + +@Component({ + selector: 'gf-x-ray-page', + styleUrl: './x-ray-page.component.scss', + templateUrl: './x-ray-page.component.html' +}) +export class XRayPageComponent { + public accountClusterRiskRules: PortfolioReportRule[]; + public currencyClusterRiskRules: PortfolioReportRule[]; + public economicMarketClusterRiskRules: PortfolioReportRule[]; + public emergencyFundRules: PortfolioReportRule[]; + public feeRules: PortfolioReportRule[]; + public hasImpersonationId: boolean; + public hasPermissionToUpdateUserSettings: boolean; + public inactiveRules: PortfolioReportRule[]; + public isLoadingPortfolioReport = false; + public user: User; + + private unsubscribeSubject = new Subject(); + + public constructor( + private changeDetectorRef: ChangeDetectorRef, + private dataService: DataService, + private impersonationStorageService: ImpersonationStorageService, + private userService: UserService + ) {} + + public ngOnInit() { + this.impersonationStorageService + .onChangeHasImpersonation() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((impersonationId) => { + this.hasImpersonationId = !!impersonationId; + }); + + this.userService.stateChanged + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((state) => { + if (state?.user) { + this.user = state.user; + + this.hasPermissionToUpdateUserSettings = + this.user.subscription?.type === 'Basic' + ? false + : hasPermission( + this.user.permissions, + permissions.updateUserSettings + ); + + this.changeDetectorRef.markForCheck(); + } + }); + + this.initializePortfolioReport(); + } + + public onRulesUpdated(event: UpdateUserSettingDto) { + this.dataService + .putUserSetting(event) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(() => { + this.userService + .get(true) + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(); + + this.initializePortfolioReport(); + }); + } + + public ngOnDestroy() { + this.unsubscribeSubject.next(); + this.unsubscribeSubject.complete(); + } + + private initializePortfolioReport() { + this.isLoadingPortfolioReport = true; + + this.dataService + .fetchPortfolioReport() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe((portfolioReport) => { + this.inactiveRules = this.mergeInactiveRules(portfolioReport); + + this.accountClusterRiskRules = + portfolioReport.rules['accountClusterRisk']?.filter( + ({ isActive }) => { + return isActive; + } + ) ?? null; + + this.currencyClusterRiskRules = + portfolioReport.rules['currencyClusterRisk']?.filter( + ({ isActive }) => { + return isActive; + } + ) ?? null; + + this.economicMarketClusterRiskRules = + portfolioReport.rules['economicMarketClusterRisk']?.filter( + ({ isActive }) => { + return isActive; + } + ) ?? null; + + this.emergencyFundRules = + portfolioReport.rules['emergencyFund']?.filter(({ isActive }) => { + return isActive; + }) ?? null; + + this.feeRules = + portfolioReport.rules['fees']?.filter(({ isActive }) => { + return isActive; + }) ?? null; + + this.isLoadingPortfolioReport = false; + + this.changeDetectorRef.markForCheck(); + }); + } + + private mergeInactiveRules(report: PortfolioReport): PortfolioReportRule[] { + let inactiveRules: PortfolioReportRule[] = []; + + for (const category in report.rules) { + const rulesArray = report.rules[category]; + + inactiveRules = inactiveRules.concat( + rulesArray.filter(({ isActive }) => { + return !isActive; + }) + ); + } + + return inactiveRules; + } +} diff --git a/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.module.ts b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.module.ts new file mode 100644 index 000000000..bff4f4dc9 --- /dev/null +++ b/apps/client/src/app/pages/portfolio/x-ray/x-ray-page.module.ts @@ -0,0 +1,22 @@ +import { GfRulesModule } from '@ghostfolio/client/components/rules/rules.module'; +import { GfPremiumIndicatorComponent } from '@ghostfolio/ui/premium-indicator'; + +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; + +import { XRayPageRoutingModule } from './x-ray-page-routing.module'; +import { XRayPageComponent } from './x-ray-page.component'; + +@NgModule({ + declarations: [XRayPageComponent], + imports: [ + CommonModule, + GfPremiumIndicatorComponent, + GfRulesModule, + NgxSkeletonLoaderModule, + XRayPageRoutingModule + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +export class XRayPageModule {} diff --git a/apps/client/src/app/pages/pricing/pricing-page.component.ts b/apps/client/src/app/pages/pricing/pricing-page.component.ts index 8bd0f1bd5..f86a75904 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.component.ts +++ b/apps/client/src/app/pages/pricing/pricing-page.component.ts @@ -6,6 +6,7 @@ import { hasPermission, permissions } from '@ghostfolio/common/permissions'; import { translate } from '@ghostfolio/ui/i18n'; import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { StringValue } from 'ms'; import { StripeService } from 'ngx-stripe'; import { Subject } from 'rxjs'; import { catchError, switchMap, takeUntil } from 'rxjs/operators'; @@ -20,6 +21,7 @@ export class PricingPageComponent implements OnDestroy, OnInit { public baseCurrency: string; public coupon: number; public couponId: string; + public durationExtension: StringValue; public hasPermissionToUpdateUserSettings: boolean; public importAndExportTooltipBasic = translate( 'DATA_IMPORT_AND_EXPORT_TOOLTIP_BASIC' @@ -51,11 +53,12 @@ export class PricingPageComponent implements OnDestroy, OnInit { ) {} public ngOnInit() { - const { baseCurrency, subscriptions } = this.dataService.fetchInfo(); + const { baseCurrency, subscriptionOffers } = this.dataService.fetchInfo(); this.baseCurrency = baseCurrency; - this.coupon = subscriptions?.default?.coupon; - this.price = subscriptions?.default?.price; + this.coupon = subscriptionOffers?.default?.coupon; + this.durationExtension = subscriptionOffers?.default?.durationExtension; + this.price = subscriptionOffers?.default?.price; this.userService.stateChanged .pipe(takeUntil(this.unsubscribeSubject)) @@ -68,11 +71,18 @@ export class PricingPageComponent implements OnDestroy, OnInit { permissions.updateUserSettings ); - this.coupon = subscriptions?.[this.user?.subscription?.offer]?.coupon; + this.coupon = + subscriptionOffers?.[this.user?.subscription?.offer]?.coupon; this.couponId = - subscriptions?.[this.user.subscription.offer]?.couponId; - this.price = subscriptions?.[this.user?.subscription?.offer]?.price; - this.priceId = subscriptions?.[this.user.subscription.offer]?.priceId; + subscriptionOffers?.[this.user.subscription.offer]?.couponId; + this.durationExtension = + subscriptionOffers?.[ + this.user?.subscription?.offer + ]?.durationExtension; + this.price = + subscriptionOffers?.[this.user?.subscription?.offer]?.price; + this.priceId = + subscriptionOffers?.[this.user.subscription.offer]?.priceId; this.changeDetectorRef.markForCheck(); } diff --git a/apps/client/src/app/pages/pricing/pricing-page.html b/apps/client/src/app/pages/pricing/pricing-page.html index fe805ef62..605ad5d2e 100644 --- a/apps/client/src/app/pages/pricing/pricing-page.html +++ b/apps/client/src/app/pages/pricing/pricing-page.html @@ -101,6 +101,11 @@

    } + @if (durationExtension) { + + } @@ -159,6 +164,11 @@

    } + @if (durationExtension) { + + } @@ -289,6 +299,14 @@

    } + @if (durationExtension) { +
    +
    + Limited Offer! Get + {{ durationExtension }} extra +
    +
    + } 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 ea14bbc6b..39dbc4813 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 @@ -35,9 +35,9 @@ export class GfProductPageComponent implements OnInit { ) {} public ngOnInit() { - const { subscriptions } = this.dataService.fetchInfo(); + const { subscriptionOffers } = this.dataService.fetchInfo(); - this.price = subscriptions?.default?.price; + this.price = subscriptionOffers?.default?.price; this.product1 = { founded: 2021, diff --git a/apps/client/src/app/services/data.service.ts b/apps/client/src/app/services/data.service.ts index 8a6d087d1..ac9772352 100644 --- a/apps/client/src/app/services/data.service.ts +++ b/apps/client/src/app/services/data.service.ts @@ -10,7 +10,6 @@ import { } from '@ghostfolio/api/app/order/interfaces/activities.interface'; import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto'; import { PortfolioHoldingDetail } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-holding-detail.interface'; -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { SymbolItem } from '@ghostfolio/api/app/symbol/interfaces/symbol-item.interface'; import { DeleteOwnUserDto } from '@ghostfolio/api/app/user/delete-own-user.dto'; import { UserItem } from '@ghostfolio/api/app/user/interfaces/user-item.interface'; @@ -30,6 +29,7 @@ import { Filter, ImportResponse, InfoItem, + LookupResponse, OAuthResponse, PortfolioDetails, PortfolioDividends, @@ -464,10 +464,10 @@ export class DataService { } return this.http - .get<{ items: LookupItem[] }>('/api/v1/symbol/lookup', { params }) + .get('/api/v1/symbol/lookup', { params }) .pipe( - map((respose) => { - return respose.items; + map(({ items }) => { + return items; }) ); } @@ -535,7 +535,7 @@ export class DataService { }: { filters?: Filter[]; range?: DateRange; - }) { + } = {}) { let params = this.buildFiltersAsQueryParams({ filters }); if (range) { diff --git a/apps/client/src/app/services/user/user.service.ts b/apps/client/src/app/services/user/user.service.ts index 3f550bb01..184df97a3 100644 --- a/apps/client/src/app/services/user/user.service.ts +++ b/apps/client/src/app/services/user/user.service.ts @@ -65,6 +65,20 @@ export class UserService extends ObservableStore { }); } + if (user?.settings['filters.dataSource']) { + filters.push({ + id: user.settings['filters.dataSource'], + type: 'DATA_SOURCE' + }); + } + + if (user?.settings['filters.symbol']) { + filters.push({ + id: user.settings['filters.symbol'], + type: 'SYMBOL' + }); + } + if (user?.settings['filters.tags']) { filters.push({ id: user.settings['filters.tags'].join(','), diff --git a/apps/client/src/locales/messages.it.xlf b/apps/client/src/locales/messages.it.xlf index 9364d8356..9654329eb 100644 --- a/apps/client/src/locales/messages.it.xlf +++ b/apps/client/src/locales/messages.it.xlf @@ -2872,7 +2872,7 @@ Hello, has shared a Portfolio with you! - Salve, ha condiviso un Portafoglio con te! + Salve, ha condiviso un Portafoglio con te! apps/client/src/app/pages/public/public-page.html 4 @@ -5881,7 +5881,7 @@ Currency Cluster Risks - Currency Cluster Risks + Rischio di Concentrazione Valutario apps/client/src/app/pages/portfolio/fire/fire-page.html 141 @@ -5889,7 +5889,7 @@ Account Cluster Risks - Account Cluster Risks + Rischi di Concentrazione dei Conti apps/client/src/app/pages/portfolio/fire/fire-page.html 160 @@ -6237,7 +6237,7 @@ Restricted view - Restricted view + Vista limitata apps/client/src/app/components/access-table/access-table.component.html 26 @@ -6357,7 +6357,7 @@ WTD - WTD + Settimana corrente libs/ui/src/lib/assistant/assistant.component.ts 212 @@ -6373,7 +6373,7 @@ MTD - MTD + Mese corrente libs/ui/src/lib/assistant/assistant.component.ts 216 @@ -6597,7 +6597,7 @@ {VAR_PLURAL, plural, =1 {activity} other {activities}} - {VAR_PLURAL, plural, =1 {activity} other {activities}} + {VAR_PLURAL, plural, =1 {attività} other {attività}} apps/client/src/app/components/portfolio-summary/portfolio-summary.component.html 14 @@ -6781,7 +6781,7 @@ Family Office - Family Office + Ufficio familiare apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 87 @@ -6837,7 +6837,7 @@ User Experience - User Experience + Esperienza Utente apps/client/src/app/pages/resources/personal-finance-tools/product-page.component.ts 98 @@ -7061,7 +7061,7 @@ Copy link to clipboard - Copy link to clipboard + Copia link negli appunti apps/client/src/app/components/access-table/access-table.component.html 70 @@ -7069,7 +7069,7 @@ Portfolio Snapshot - Portfolio Snapshot + Stato del Portfolio apps/client/src/app/components/admin-jobs/admin-jobs.html 39 @@ -7077,7 +7077,7 @@ Change with currency effect Change - Change with currency effect Change + Cambio con effetto valuta Cambia apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 50 @@ -7085,7 +7085,7 @@ Performance with currency effect Performance - Performance with currency effect Performance + Prestazioni con effetto valuta Prestazioni apps/client/src/app/components/holding-detail-dialog/holding-detail-dialog.html 69 @@ -7093,7 +7093,7 @@ Threshold Min - Threshold Min + Soglia Minima apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html 9 @@ -7101,7 +7101,7 @@ Threshold Max - Threshold Max + Soglia Massima apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html 44 @@ -7109,7 +7109,7 @@ Close - Close + Chiudi apps/client/src/app/components/rule/rule-settings-dialog/rule-settings-dialog.html 77 @@ -7117,7 +7117,7 @@ Customize - Customize + Personalizza apps/client/src/app/components/rule/rule.component.html 67 @@ -7125,7 +7125,7 @@ No auto-renewal. - No auto-renewal. + No rinnovo automatico. apps/client/src/app/components/user-account-membership/user-account-membership.html 62 @@ -7133,7 +7133,7 @@ Today - Today + Oggi apps/client/src/app/pages/public/public-page.html 24 @@ -7141,7 +7141,7 @@ This year - This year + Anno corrente apps/client/src/app/pages/public/public-page.html 42 @@ -7149,7 +7149,7 @@ From the beginning - From the beginning + Dall'inizio apps/client/src/app/pages/public/public-page.html 60 @@ -7157,7 +7157,7 @@ Oops! Invalid currency. - Oops! Invalid currency. + Oops! Valuta sbagliata. apps/client/src/app/components/admin-market-data/create-asset-profile-dialog/create-asset-profile-dialog.html 49 @@ -7165,7 +7165,7 @@ This page has been archived. - This page has been archived. + Questa pagina è stata archiviata. apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 14 @@ -7173,7 +7173,7 @@ is Open Source Software - is Open Source Software + è un programma Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 139 @@ -7181,7 +7181,7 @@ is not Open Source Software - is not Open Source Software + non è un programma Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 146 @@ -7189,7 +7189,7 @@ is Open Source Software - is Open Source Software + è un programma Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 156 @@ -7197,7 +7197,7 @@ is not Open Source Software - is not Open Source Software + non è un programma Open Source apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 163 @@ -7205,7 +7205,7 @@ can be self-hosted - can be self-hosted + può essere ospitato in proprio apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 178 @@ -7213,7 +7213,7 @@ cannot be self-hosted - cannot be self-hosted + non può essere ospitato in proprio apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 185 @@ -7221,7 +7221,7 @@ can be self-hosted - can be self-hosted + può essere ospitato in proprio apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 195 @@ -7229,7 +7229,7 @@ cannot be self-hosted - cannot be self-hosted + non può essere ospitato in proprio apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 202 @@ -7237,7 +7237,7 @@ can be used anonymously - can be used anonymously + può essere usato anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 217 @@ -7245,7 +7245,7 @@ cannot be used anonymously - cannot be used anonymously + non può essere usato anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 224 @@ -7253,7 +7253,7 @@ can be used anonymously - can be used anonymously + può essere usato anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 234 @@ -7261,7 +7261,7 @@ cannot be used anonymously - cannot be used anonymously + non può essere usato anonimamente apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 241 @@ -7269,7 +7269,7 @@ offers a free plan - offers a free plan + ha un piano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 256 @@ -7277,7 +7277,7 @@ does not offer a free plan - does not offer a free plan + non ha un piano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 263 @@ -7285,7 +7285,7 @@ offers a free plan - offers a free plan + ha un piano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 273 @@ -7293,7 +7293,7 @@ does not offer a free plan - does not offer a free plan + non ha un piano gratuito apps/client/src/app/pages/resources/personal-finance-tools/product-page.html 280 @@ -7301,7 +7301,7 @@ Oops! Could not find any assets. - Oops! Could not find any assets. + Oops! Non ho trovato alcun asset. libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.html 37 @@ -7309,7 +7309,7 @@ Data Providers - Data Providers + Fornitori di dati apps/client/src/app/components/admin-settings/admin-settings.component.html 4 @@ -7317,7 +7317,7 @@ NEW - NEW + NUOVO apps/client/src/app/components/admin-settings/admin-settings.component.html 14 @@ -7325,7 +7325,7 @@ Set API Key - Set API Key + Imposta API Key apps/client/src/app/components/admin-settings/admin-settings.component.html 29 @@ -7333,7 +7333,7 @@ Want to stay updated? Click below to get notified as soon as it’s available. - Want to stay updated? Click below to get notified as soon as it’s available. + Vuoi seguire le novità? Clicca sotto per essere notificato appena è disponibile. apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.html 23 @@ -7341,7 +7341,7 @@ Notify me - Notify me + Notificami apps/client/src/app/components/admin-settings/ghostfolio-premium-api-dialog/ghostfolio-premium-api-dialog.html 32 @@ -7349,7 +7349,7 @@ Get access to 100’000+ tickers from over 50 exchanges - Get access to 100’000+ tickers from over 50 exchanges + Ottieni accesso a oltre 100’000+ titoli da oltre 50 borse libs/ui/src/lib/i18n.ts 24 @@ -7357,7 +7357,7 @@ Ukraine - Ukraine + Ucraina libs/ui/src/lib/i18n.ts 92 @@ -7365,7 +7365,7 @@ Skip - Skip + Salta apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html 83 @@ -7373,7 +7373,7 @@ Join now - Join now + Iscriviti adesso apps/client/src/app/components/subscription-interstitial-dialog/subscription-interstitial-dialog.html 93 @@ -7381,7 +7381,7 @@ Allocation Cluster Risks - Allocation Cluster Risks + Rischi di allocazione dei Conti apps/client/src/app/pages/portfolio/fire/fire-page.html 179 @@ -7389,7 +7389,7 @@ Glossary - Glossary + Glossario apps/client/src/app/pages/resources/glossary/resources-glossary-routing.module.ts 10 @@ -7401,7 +7401,7 @@ Guides - Guides + Guide apps/client/src/app/pages/resources/guides/resources-guides-routing.module.ts 10 @@ -7413,7 +7413,7 @@ guides - guides + guide snake-case apps/client/src/app/pages/resources/overview/resources-overview.component.ts @@ -7426,7 +7426,7 @@ glossary - glossary + glossario snake-case apps/client/src/app/pages/resources/overview/resources-overview.component.ts @@ -7439,4 +7439,4 @@ - + \ No newline at end of file diff --git a/docker/docker-compose.build.yml b/docker/docker-compose.build.yml index 02442c0c2..96829ad34 100644 --- a/docker/docker-compose.build.yml +++ b/docker/docker-compose.build.yml @@ -1,6 +1,8 @@ +name: ghostfolio_build services: ghostfolio: build: ../ + container_name: ghostfolio-build init: true env_file: - ../.env @@ -23,6 +25,7 @@ services: postgres: image: docker.io/library/postgres:15 + container_name: gf-postgres-build env_file: - ../.env healthcheck: @@ -35,6 +38,7 @@ services: redis: image: docker.io/library/redis:alpine + container_name: gf-redis-build env_file: - ../.env command: ['redis-server', '--requirepass', $REDIS_PASSWORD] diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 91f8f2c09..39a1d56e9 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -1,7 +1,8 @@ +name: ghostfolio_dev services: postgres: image: docker.io/library/postgres:15 - container_name: postgres + container_name: gf-postgres-dev restart: unless-stopped env_file: - ../.env @@ -12,7 +13,7 @@ services: redis: image: docker.io/library/redis:alpine - container_name: redis + container_name: gf-redis-dev restart: unless-stopped env_file: - ../.env diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 642912b73..c6ec5b3d7 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,6 +1,8 @@ +name: ghostfolio services: ghostfolio: image: docker.io/ghostfolio/ghostfolio:latest + container_name: ghostfolio init: true cap_drop: - ALL @@ -27,6 +29,7 @@ services: postgres: image: docker.io/library/postgres:15 + container_name: gf-postgres cap_drop: - ALL cap_add: @@ -49,6 +52,7 @@ services: redis: image: docker.io/library/redis:alpine + container_name: gf-redis user: '999:1000' cap_drop: - ALL diff --git a/libs/common/src/lib/interfaces/index.ts b/libs/common/src/lib/interfaces/index.ts index 0ec04594c..eca147066 100644 --- a/libs/common/src/lib/interfaces/index.ts +++ b/libs/common/src/lib/interfaces/index.ts @@ -23,6 +23,7 @@ import type { Holding } from './holding.interface'; import type { InfoItem } from './info-item.interface'; import type { InvestmentItem } from './investment-item.interface'; import type { LineChartItem } from './line-chart-item.interface'; +import type { LookupItem } from './lookup-item.interface'; import type { PortfolioChart } from './portfolio-chart.interface'; import type { PortfolioDetails } from './portfolio-details.interface'; import type { PortfolioDividends } from './portfolio-dividends.interface'; @@ -40,13 +41,14 @@ import type { AccountBalancesResponse } from './responses/account-balances-respo import type { BenchmarkResponse } from './responses/benchmark-response.interface'; import type { ResponseError } from './responses/errors.interface'; import type { ImportResponse } from './responses/import-response.interface'; +import type { LookupResponse } from './responses/lookup-response.interface'; import type { OAuthResponse } from './responses/oauth-response.interface'; import type { PortfolioHoldingsResponse } from './responses/portfolio-holdings-response.interface'; import type { PortfolioPerformanceResponse } from './responses/portfolio-performance-response.interface'; import type { PublicPortfolioResponse } from './responses/public-portfolio-response.interface'; import type { ScraperConfiguration } from './scraper-configuration.interface'; import type { Statistics } from './statistics.interface'; -import type { Subscription } from './subscription.interface'; +import type { SubscriptionOffer } from './subscription-offer.interface'; import type { SymbolMetrics } from './symbol-metrics.interface'; import type { SystemMessage } from './system-message.interface'; import type { TabConfiguration } from './tab-configuration.interface'; @@ -82,6 +84,8 @@ export { InfoItem, InvestmentItem, LineChartItem, + LookupItem, + LookupResponse, OAuthResponse, PortfolioChart, PortfolioDetails, @@ -102,8 +106,8 @@ export { ResponseError, ScraperConfiguration, Statistics, + SubscriptionOffer, SystemMessage, - Subscription, SymbolMetrics, TabConfiguration, ToggleOption, diff --git a/libs/common/src/lib/interfaces/info-item.interface.ts b/libs/common/src/lib/interfaces/info-item.interface.ts index 1b3926331..bd3eb1f94 100644 --- a/libs/common/src/lib/interfaces/info-item.interface.ts +++ b/libs/common/src/lib/interfaces/info-item.interface.ts @@ -1,9 +1,9 @@ -import { SubscriptionOffer } from '@ghostfolio/common/types'; +import { SubscriptionOfferKey } from '@ghostfolio/common/types'; import { Platform, SymbolProfile } from '@prisma/client'; import { Statistics } from './statistics.interface'; -import { Subscription } from './subscription.interface'; +import { SubscriptionOffer } from './subscription-offer.interface'; export interface InfoItem { baseCurrency: string; @@ -18,5 +18,5 @@ export interface InfoItem { platforms: Platform[]; statistics: Statistics; stripePublicKey?: string; - subscriptions: { [offer in SubscriptionOffer]: Subscription }; + subscriptionOffers: { [offer in SubscriptionOfferKey]: SubscriptionOffer }; } diff --git a/apps/api/src/app/symbol/interfaces/lookup-item.interface.ts b/libs/common/src/lib/interfaces/lookup-item.interface.ts similarity index 80% rename from apps/api/src/app/symbol/interfaces/lookup-item.interface.ts rename to libs/common/src/lib/interfaces/lookup-item.interface.ts index 89b476397..fa91ed690 100644 --- a/apps/api/src/app/symbol/interfaces/lookup-item.interface.ts +++ b/libs/common/src/lib/interfaces/lookup-item.interface.ts @@ -1,7 +1,7 @@ -import { DataProviderInfo } from '@ghostfolio/common/interfaces'; - import { AssetClass, AssetSubClass, DataSource } from '@prisma/client'; +import { DataProviderInfo } from './data-provider-info.interface'; + export interface LookupItem { assetClass: AssetClass; assetSubClass: AssetSubClass; diff --git a/libs/common/src/lib/interfaces/responses/lookup-response.interface.ts b/libs/common/src/lib/interfaces/responses/lookup-response.interface.ts new file mode 100644 index 000000000..579be9d01 --- /dev/null +++ b/libs/common/src/lib/interfaces/responses/lookup-response.interface.ts @@ -0,0 +1,5 @@ +import { LookupItem } from '../lookup-item.interface'; + +export interface LookupResponse { + items: LookupItem[]; +} diff --git a/libs/common/src/lib/interfaces/subscription-offer.interface.ts b/libs/common/src/lib/interfaces/subscription-offer.interface.ts new file mode 100644 index 000000000..8db91da6e --- /dev/null +++ b/libs/common/src/lib/interfaces/subscription-offer.interface.ts @@ -0,0 +1,9 @@ +import { StringValue } from 'ms'; + +export interface SubscriptionOffer { + coupon?: number; + couponId?: string; + durationExtension?: StringValue; + price: number; + priceId: string; +} diff --git a/libs/common/src/lib/interfaces/subscription.interface.ts b/libs/common/src/lib/interfaces/subscription.interface.ts deleted file mode 100644 index 29f5d3aba..000000000 --- a/libs/common/src/lib/interfaces/subscription.interface.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface Subscription { - coupon?: number; - couponId?: string; - price: number; - priceId: string; -} diff --git a/libs/common/src/lib/interfaces/user-settings.interface.ts b/libs/common/src/lib/interfaces/user-settings.interface.ts index e9e90e71f..d72be7c7c 100644 --- a/libs/common/src/lib/interfaces/user-settings.interface.ts +++ b/libs/common/src/lib/interfaces/user-settings.interface.ts @@ -14,6 +14,8 @@ export interface UserSettings { dateRange?: DateRange; emergencyFund?: number; 'filters.accounts'?: string[]; + 'filters.dataSource'?: string; + 'filters.symbol'?: string; 'filters.tags'?: string[]; holdingsViewMode?: HoldingsViewMode; isExperimentalFeatures?: boolean; diff --git a/libs/common/src/lib/interfaces/user.interface.ts b/libs/common/src/lib/interfaces/user.interface.ts index 27cd1a610..647822d34 100644 --- a/libs/common/src/lib/interfaces/user.interface.ts +++ b/libs/common/src/lib/interfaces/user.interface.ts @@ -1,4 +1,4 @@ -import { SubscriptionOffer } from '@ghostfolio/common/types'; +import { SubscriptionOfferKey } from '@ghostfolio/common/types'; import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type'; import { Account, Tag } from '@prisma/client'; @@ -20,7 +20,7 @@ export interface User { systemMessage?: SystemMessage; subscription: { expiresAt?: Date; - offer: SubscriptionOffer; + offer: SubscriptionOfferKey; type: SubscriptionType; }; tags: (Tag & { isUsed: boolean })[]; diff --git a/libs/common/src/lib/types/index.ts b/libs/common/src/lib/types/index.ts index a66755ab1..9e8178d3c 100644 --- a/libs/common/src/lib/types/index.ts +++ b/libs/common/src/lib/types/index.ts @@ -15,7 +15,7 @@ import type { MarketState } from './market-state.type'; import type { Market } from './market.type'; import type { OrderWithAccount } from './order-with-account.type'; import type { RequestWithUser } from './request-with-user.type'; -import type { SubscriptionOffer } from './subscription-offer.type'; +import type { SubscriptionOfferKey } from './subscription-offer-key.type'; import type { UserWithSettings } from './user-with-settings.type'; import type { ViewMode } from './view-mode.type'; @@ -37,7 +37,7 @@ export type { MarketState, OrderWithAccount, RequestWithUser, - SubscriptionOffer, + SubscriptionOfferKey, UserWithSettings, ViewMode }; diff --git a/libs/common/src/lib/types/subscription-offer.type.ts b/libs/common/src/lib/types/subscription-offer-key.type.ts similarity index 71% rename from libs/common/src/lib/types/subscription-offer.type.ts rename to libs/common/src/lib/types/subscription-offer-key.type.ts index 98977da45..f6d898a01 100644 --- a/libs/common/src/lib/types/subscription-offer.type.ts +++ b/libs/common/src/lib/types/subscription-offer-key.type.ts @@ -1,4 +1,4 @@ -export type SubscriptionOffer = +export type SubscriptionOfferKey = | 'default' | 'renewal' | 'renewal-early-bird-2023' diff --git a/libs/common/src/lib/types/user-with-settings.type.ts b/libs/common/src/lib/types/user-with-settings.type.ts index 59e9f142d..2a669d26f 100644 --- a/libs/common/src/lib/types/user-with-settings.type.ts +++ b/libs/common/src/lib/types/user-with-settings.type.ts @@ -1,5 +1,5 @@ import { UserSettings } from '@ghostfolio/common/interfaces'; -import { SubscriptionOffer } from '@ghostfolio/common/types'; +import { SubscriptionOfferKey } from '@ghostfolio/common/types'; import { SubscriptionType } from '@ghostfolio/common/types/subscription-type.type'; import { Access, Account, Settings, User } from '@prisma/client'; @@ -13,7 +13,7 @@ export type UserWithSettings = User & { Settings: Settings & { settings: UserSettings }; subscription?: { expiresAt?: Date; - offer: SubscriptionOffer; + offer: SubscriptionOfferKey; type: SubscriptionType; }; }; diff --git a/libs/ui/src/lib/assistant/assistant.component.ts b/libs/ui/src/lib/assistant/assistant.component.ts index 4f2f2a341..b87390d58 100644 --- a/libs/ui/src/lib/assistant/assistant.component.ts +++ b/libs/ui/src/lib/assistant/assistant.component.ts @@ -1,7 +1,9 @@ import { GfAssetProfileIconComponent } from '@ghostfolio/client/components/asset-profile-icon/asset-profile-icon.component'; +import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { AdminService } from '@ghostfolio/client/services/admin.service'; import { DataService } from '@ghostfolio/client/services/data.service'; -import { Filter, User } from '@ghostfolio/common/interfaces'; +import { getAssetProfileIdentifier } from '@ghostfolio/common/helper'; +import { Filter, PortfolioPosition, User } from '@ghostfolio/common/interfaces'; import { DateRange } from '@ghostfolio/common/types'; import { translate } from '@ghostfolio/ui/i18n'; @@ -35,7 +37,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatMenuTrigger } from '@angular/material/menu'; import { MatSelectModule } from '@angular/material/select'; import { RouterModule } from '@angular/router'; -import { Account, AssetClass } from '@prisma/client'; +import { Account, AssetClass, DataSource } from '@prisma/client'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { EMPTY, Observable, Subject, lastValueFrom } from 'rxjs'; import { @@ -61,6 +63,7 @@ import { FormsModule, GfAssetProfileIconComponent, GfAssistantListItemComponent, + GfSymbolModule, MatButtonModule, MatFormFieldModule, MatSelectModule, @@ -130,10 +133,12 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { public dateRangeFormControl = new FormControl(undefined); public dateRangeOptions: IDateRangeOption[] = []; public filterForm = this.formBuilder.group({ - account: new FormControl(undefined), - assetClass: new FormControl(undefined), - tag: new FormControl(undefined) + account: new FormControl(undefined), + assetClass: new FormControl(undefined), + holding: new FormControl(undefined), + tag: new FormControl(undefined) }); + public holdings: PortfolioPosition[] = []; public isLoading = false; public isOpen = false; public placeholder = $localize`Find holding...`; @@ -144,7 +149,13 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }; public tags: Filter[] = []; - private filterTypes: Filter['type'][] = ['ACCOUNT', 'ASSET_CLASS', 'TAG']; + private filterTypes: Filter['type'][] = [ + 'ACCOUNT', + 'ASSET_CLASS', + 'DATA_SOURCE', + 'SYMBOL', + 'TAG' + ]; private keyManager: FocusKeyManager; private unsubscribeSubject = new Subject(); @@ -156,6 +167,8 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { ) {} public ngOnInit() { + this.initializeFilterForm(); + this.assetClasses = Object.keys(AssetClass).map((assetClass) => { return { id: assetClass, @@ -263,16 +276,7 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { this.filterForm.enable({ emitEvent: false }); } - this.filterForm.setValue( - { - account: this.user?.settings?.['filters.accounts'] ?? null, - assetClass: this.user?.settings?.['filters.assetClasses'] ?? null, - tag: this.user?.settings?.['filters.tags'] ?? null - }, - { - emitEvent: false - } - ); + this.initializeFilterForm(); this.tags = this.user?.tags @@ -298,6 +302,19 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { }); } + public holdingComparisonFunction( + option: PortfolioPosition, + value: PortfolioPosition + ): boolean { + if (value === null) { + return false; + } + + return ( + getAssetProfileIdentifier(option) === getAssetProfileIdentifier(value) + ); + } + public async initialize() { this.isLoading = true; this.keyManager = new FocusKeyManager(this.assistantListItems).withWrap(); @@ -322,28 +339,28 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { } public onApplyFilters() { - const accountFilters = - this.filterForm - .get('account') - .value?.reduce( - (arr, val) => [...arr, { id: val, type: 'ACCOUNT' }], - [] - ) ?? []; - const assetClassFilters = - this.filterForm - .get('assetClass') - .value?.reduce( - (arr, val) => [...arr, { id: val, type: 'ASSET_CLASS' }], - [] - ) ?? []; - const tagFilters = - this.filterForm - .get('tag') - .value?.reduce((arr, val) => [...arr, { id: val, type: 'TAG' }], []) ?? - []; - let filters = [...accountFilters, ...assetClassFilters]; - filters = [...filters, ...tagFilters]; - this.filtersChanged.emit(filters); + this.filtersChanged.emit([ + { + id: this.filterForm.get('account').value, + type: 'ACCOUNT' + }, + { + id: this.filterForm.get('assetClass').value, + type: 'ASSET_CLASS' + }, + { + id: this.filterForm.get('holding').value?.dataSource, + type: 'DATA_SOURCE' + }, + { + id: this.filterForm.get('holding').value?.symbol, + type: 'SYMBOL' + }, + { + id: this.filterForm.get('tag').value, + type: 'TAG' + } + ]); this.onCloseAssistant(); } @@ -481,4 +498,47 @@ export class GfAssistantComponent implements OnChanges, OnDestroy, OnInit { takeUntil(this.unsubscribeSubject) ); } + + private initializeFilterForm() { + this.dataService + .fetchPortfolioHoldings() + .pipe(takeUntil(this.unsubscribeSubject)) + .subscribe(({ holdings }) => { + this.holdings = holdings + .filter(({ assetSubClass }) => { + return !['CASH'].includes(assetSubClass); + }) + .sort((a, b) => { + return a.name?.localeCompare(b.name); + }); + this.setFilterFormValues(); + }); + } + + private setFilterFormValues() { + const dataSource = this.user?.settings?.[ + 'filters.dataSource' + ] as DataSource; + const symbol = this.user?.settings?.['filters.symbol']; + const selectedHolding = this.holdings.find((holding) => { + return ( + getAssetProfileIdentifier({ + dataSource: holding.dataSource, + symbol: holding.symbol + }) === getAssetProfileIdentifier({ dataSource, symbol }) + ); + }); + + this.filterForm.setValue( + { + account: this.user?.settings?.['filters.accounts']?.[0] ?? null, + assetClass: this.user?.settings?.['filters.assetClasses']?.[0] ?? null, + holding: selectedHolding ?? null, + tag: this.user?.settings?.['filters.tags']?.[0] ?? null + }, + { + emitEvent: false + } + ); + } } diff --git a/libs/ui/src/lib/assistant/assistant.html b/libs/ui/src/lib/assistant/assistant.html index 024b270ee..2a6008510 100644 --- a/libs/ui/src/lib/assistant/assistant.html +++ b/libs/ui/src/lib/assistant/assistant.html @@ -122,6 +122,34 @@ +
    + + Holding + + {{ + filterForm.get('holding')?.value?.name + }} + + @for (holding of holdings; track holding.name) { + +
    + {{ holding.name }} +
    + {{ holding.symbol | gfSymbol }} · + {{ holding.currency }} +
    +
    + } +
    +
    +
    Tags diff --git a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts index da97aac05..a537c50a7 100644 --- a/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts +++ b/libs/ui/src/lib/symbol-autocomplete/symbol-autocomplete.component.ts @@ -1,6 +1,6 @@ -import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface'; import { GfSymbolModule } from '@ghostfolio/client/pipes/symbol/symbol.module'; import { DataService } from '@ghostfolio/client/services/data.service'; +import { LookupItem } from '@ghostfolio/common/interfaces'; import { translate } from '@ghostfolio/ui/i18n'; import { AbstractMatFormField } from '@ghostfolio/ui/shared/abstract-mat-form-field'; diff --git a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts index 1acc2c925..9a8594ada 100644 --- a/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts +++ b/libs/ui/src/lib/treemap-chart/treemap-chart.component.ts @@ -261,12 +261,21 @@ export class GfTreemapChartComponent display: true, font: [{ size: 16 }, { lineHeight: 1.5, size: 14 }], formatter: (ctx) => { - const netPerformancePercentWithCurrencyEffect = - ctx.raw._data.netPerformancePercentWithCurrencyEffect; + // Round to 4 decimal places + let netPerformancePercentWithCurrencyEffect = + Math.round( + ctx.raw._data.netPerformancePercentWithCurrencyEffect * 10000 + ) / 10000; + + if (Math.abs(netPerformancePercentWithCurrencyEffect) === 0) { + netPerformancePercentWithCurrencyEffect = Math.abs( + netPerformancePercentWithCurrencyEffect + ); + } return [ ctx.raw._data.symbol, - `${netPerformancePercentWithCurrencyEffect > 0 ? '+' : ''}${(ctx.raw._data.netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%` + `${netPerformancePercentWithCurrencyEffect > 0 ? '+' : ''}${(netPerformancePercentWithCurrencyEffect * 100).toFixed(2)}%` ]; }, hoverColor: undefined, diff --git a/package-lock.json b/package-lock.json index 12f589e25..beaa52766 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.119.0", + "version": "2.122.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.119.0", + "version": "2.122.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { @@ -43,7 +43,7 @@ "@prisma/client": "5.21.1", "@simplewebauthn/browser": "9.0.1", "@simplewebauthn/server": "9.0.3", - "@stripe/stripe-js": "3.5.0", + "@stripe/stripe-js": "4.9.0", "alphavantage": "2.2.0", "await-lock": "^2.2.2", "big.js": "6.2.1", @@ -62,7 +62,7 @@ "class-validator": "0.14.1", "color": "4.2.3", "countries-and-timezones": "3.4.1", - "countries-list": "3.1.0", + "countries-list": "3.1.1", "countup.js": "2.8.0", "date-fns": "3.6.0", "envalid": "7.3.1", @@ -78,8 +78,8 @@ "ng-extract-i18n-merge": "2.12.0", "ngx-device-detector": "8.0.0", "ngx-markdown": "18.0.0", - "ngx-skeleton-loader": "7.0.0", - "ngx-stripe": "18.0.0", + "ngx-skeleton-loader": "9.0.0", + "ngx-stripe": "18.1.0", "open-color": "1.9.1", "papaparse": "5.3.1", "passport": "0.7.0", @@ -87,7 +87,7 @@ "passport-jwt": "4.0.1", "reflect-metadata": "0.1.13", "rxjs": "7.5.6", - "stripe": "15.11.0", + "stripe": "17.3.0", "svgmap": "2.6.0", "twitter-api-v2": "1.14.2", "uuid": "9.0.1", @@ -1660,7 +1660,7 @@ "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -3253,6 +3253,7 @@ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.3.1.tgz", "integrity": "sha512-pVGjBIt1Y6gg3EJN8jTcfpP/+uuRksIo055oE/OBkDNcjZqVbfkWCksG1Jp4yZnj3iKWyWX8fdG/j6UDYPbFog==", "dev": true, + "license": "MIT", "dependencies": { "@emnapi/wasi-threads": "1.0.1", "tslib": "^2.4.0" @@ -3263,6 +3264,7 @@ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.4.0" } @@ -3272,6 +3274,7 @@ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz", "integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.4.0" } @@ -5261,6 +5264,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -5276,6 +5280,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5289,6 +5294,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5298,6 +5304,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5310,6 +5317,7 @@ "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.6.6.tgz", "integrity": "sha512-gGU1tjaksk5Q5X2zpVb/OmlwvKwVVjTXreuFwkK0Z+9QKM9jbu0B/tPSh6sqibPFeu1yM2HOFlOHJhvFs1PmsA==", "dev": true, + "license": "MIT", "dependencies": { "@module-federation/bridge-react-webpack-plugin": "0.6.6", "@module-federation/data-prefetch": "0.6.6", @@ -5344,6 +5352,7 @@ "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.6.6.tgz", "integrity": "sha512-ryj2twbQmo2KhwKn1xYivpaW94l5wfplDU9FwVvW0wc8hC2lJnuGhoiZqXKL7lNaBrZXge3b43Zlgx5OnFfr6A==", "dev": true, + "license": "MIT", "dependencies": { "@module-federation/sdk": "0.6.6", "find-pkg": "2.0.0", @@ -5355,6 +5364,7 @@ "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.6.6.tgz", "integrity": "sha512-45ol0fC8RS2d+0iEt5zdp0vctE2CiOfA2kCmOFz79K33occi8sKmyevfSeZGckZy54NiMnLFteIYBsyIa+g7gg==", "dev": true, + "license": "MIT", "dependencies": { "@module-federation/dts-plugin": "0.6.6", "@module-federation/managers": "0.6.6", @@ -5368,6 +5378,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -5383,6 +5394,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -5396,6 +5408,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5405,6 +5418,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5417,6 +5431,7 @@ "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.6.6.tgz", "integrity": "sha512-30X6QPrJ/eCcmUL4GQ06Z9bQwURBnJI0607Fw2ufmAbhDA0PJFtg7NFFfXzsdChms1ACVbgvgfBH8SJg8j3wBg==", "dev": true, + "license": "MIT", "dependencies": { "@module-federation/bridge-react-webpack-plugin": "0.6.6", "@module-federation/dts-plugin": "0.6.6", @@ -5443,6 +5458,7 @@ "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.6.6.tgz", "integrity": "sha512-QsKHUV2HALRzL6mPCdJEZTDuPReKC8MMXf+/VMCtQPp6JhLEjZIO06bfEZqXMbTbTYlMzntIwu1tGCbtJRZDOQ==", "dev": true, + "license": "MIT", "dependencies": { "@module-federation/sdk": "0.6.6" } @@ -5452,6 +5468,7 @@ "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.6.6.tgz", "integrity": "sha512-w2qHa41p6rADWMS1yBjpqNhaLZ4R5oRy9OYGPe6ywjh+8oqbiBl1CfQglcgEBIpHktEjV/upsgsnjHSdJBdeZw==", "dev": true, + "license": "MIT", "dependencies": { "@module-federation/runtime": "0.6.6", "@module-federation/webpack-bundler-runtime": "0.6.6" @@ -5469,6 +5486,7 @@ "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.6.6.tgz", "integrity": "sha512-xX9p17PpElzATNEulwlJJT731xST7T7OUIDSkkIghp/ICDmZd6WhYJvNBto7xbpaj5SIB7Ocdj4chNGv0xdYPw==", "dev": true, + "license": "MIT", "dependencies": { "find-pkg": "2.0.0", "fs-extra": "9.1.0", @@ -5480,6 +5498,7 @@ "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.6.6.tgz", "integrity": "sha512-0UnY9m1fBgHwTpacYWbht1jB5X4Iqspiu1q8kfjUrv6y+R224//ydUFYYO8xfWx4V9SGQFKlU8XFH0FP/r0Hng==", "dev": true, + "license": "MIT", "dependencies": { "@module-federation/runtime": "0.6.6", "@module-federation/sdk": "0.6.6" @@ -5568,6 +5587,7 @@ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz", "integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==", "dev": true, + "license": "MIT", "dependencies": { "@emnapi/core": "^1.1.0", "@emnapi/runtime": "^1.1.0", @@ -5612,6 +5632,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-2.2.2.tgz", "integrity": "sha512-+n7rpU1QABeW2WV17Dl1vZCG3vWjJU1MaamWgZvbGxYE9EeCM0lVLfw3z7acgDTNwOy+K68xuQPoIMxD0bhjlA==", + "license": "MIT", "peerDependencies": { "@nestjs/common": "^9.0.0 || ^10.0.0", "@nestjs/core": "^9.0.0 || ^10.0.0", @@ -6413,6 +6434,7 @@ "resolved": "https://registry.npmjs.org/@nx/angular/-/angular-20.0.6.tgz", "integrity": "sha512-0UfCEp4JeQEYMpUjaipHEH/V/GRHGCd+vgPN9EdhpkSqw2YyuBXlZiX1q0DgzMxZRRBRTB+p37FgRPu32lOI6g==", "dev": true, + "license": "MIT", "dependencies": { "@module-federation/enhanced": "0.6.6", "@nx/devkit": "20.0.6", @@ -6517,6 +6539,7 @@ "resolved": "https://registry.npmjs.org/@nx/cypress/-/cypress-20.0.6.tgz", "integrity": "sha512-b26Ucgf+dAdTRlBGhFi8Xjeqw1mbUrxn3nwAOYNwuivc+CZCeokba5/orldNAlBlJKvHe0QmSAI3wpDjdU05Ww==", "dev": true, + "license": "MIT", "dependencies": { "@nx/devkit": "20.0.6", "@nx/eslint": "20.0.6", @@ -6539,6 +6562,7 @@ "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.0.6.tgz", "integrity": "sha512-vUjVVEJgfq/roCzDDZDXduwnhVXl1MM5No2UELUka2oNBK09pPigdFxzUNh8XvmOyFskCGDTLKH/dAO5yTD5Bg==", "dev": true, + "license": "MIT", "dependencies": { "ejs": "^3.1.7", "enquirer": "~2.3.6", @@ -6558,6 +6582,7 @@ "resolved": "https://registry.npmjs.org/@nx/eslint/-/eslint-20.0.6.tgz", "integrity": "sha512-07Ign5GQXZif6zHDR2oB4wkf2amSvoGhYWJ17fmqDsMF/nWYOohL+DbjAaqDORXWXL1bnmRBaj/lAkDNsmW3QA==", "dev": true, + "license": "MIT", "dependencies": { "@nx/devkit": "20.0.6", "@nx/js": "20.0.6", @@ -6580,6 +6605,7 @@ "resolved": "https://registry.npmjs.org/@nx/eslint-plugin/-/eslint-plugin-20.0.6.tgz", "integrity": "sha512-wFWg9X4dhRVY5pIAuqXLKTQSL3FzWHbV5kpg7S+y2X3jFg3pezqa8EDBkAcerSk7rour1G2hlXAfHX/W3HCrBQ==", "dev": true, + "license": "MIT", "dependencies": { "@nx/devkit": "20.0.6", "@nx/js": "20.0.6", @@ -6676,6 +6702,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6689,6 +6716,7 @@ "resolved": "https://registry.npmjs.org/@nx/jest/-/jest-20.0.6.tgz", "integrity": "sha512-/v9NavOOWcUpzgbjfYip0zipneJPhKUQd5rU3bTr0CqCJw0I+YQXotToUkzzMQYT6zmNrq7ySTMH1N8rXdy7NQ==", "dev": true, + "license": "MIT", "dependencies": { "@jest/reporters": "^29.4.1", "@jest/test-result": "^29.4.1", @@ -6768,6 +6796,7 @@ "resolved": "https://registry.npmjs.org/@nx/js/-/js-20.0.6.tgz", "integrity": "sha512-/bAMtcgKX1Te3yCzbbv+QQLnFwb6SxE0iCc6EzxiLepmGhnd0iOArUqepB1mVipfeaO37n00suFjFv1xsaqLHg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.23.2", "@babel/plugin-proposal-decorators": "^7.22.7", @@ -7055,6 +7084,7 @@ "resolved": "https://registry.npmjs.org/@nx/nest/-/nest-20.0.6.tgz", "integrity": "sha512-vACNZ+jTURJfVIpLQURgbR12O7mOqoVjCSfqbXBIC9pc4kYqShPW0SnAybwxKR+zpWGeO1nel7VXEH95HgAXuA==", "dev": true, + "license": "MIT", "dependencies": { "@nestjs/schematics": "^9.1.0", "@nx/devkit": "20.0.6", @@ -7196,6 +7226,7 @@ "resolved": "https://registry.npmjs.org/@nx/node/-/node-20.0.6.tgz", "integrity": "sha512-/6khofVKgpdglkSE6XDz9tk4kCeEXQaIPOH1PgWqY25hoim/VSXjZ1XMVmPvnvd7m2lsFLDrqZlwIGWTrT2cFw==", "dev": true, + "license": "MIT", "dependencies": { "@nx/devkit": "20.0.6", "@nx/eslint": "20.0.6", @@ -7212,6 +7243,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -7228,6 +7260,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -7244,6 +7277,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -7260,6 +7294,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -7276,6 +7311,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -7292,6 +7328,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -7308,6 +7345,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -7324,6 +7362,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -7340,6 +7379,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -7356,6 +7396,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -7369,6 +7410,7 @@ "resolved": "https://registry.npmjs.org/@nx/storybook/-/storybook-20.0.6.tgz", "integrity": "sha512-eqQKs67bRb9vutCt+dcR5CUhnSiQ2X82cYNryHEu/u8qE0LRfmCxxWh1DUNGxz1v1SYquo6RBo0qORm8oef3Pg==", "dev": true, + "license": "MIT", "dependencies": { "@nx/cypress": "20.0.6", "@nx/devkit": "20.0.6", @@ -7384,6 +7426,7 @@ "resolved": "https://registry.npmjs.org/@nx/web/-/web-20.0.6.tgz", "integrity": "sha512-lYu9FddREZYbjbjS9YYnXu+uGQUB6MptNvPNSvYRRUcdq7c8Kh10P21YyK2Ox7FsEUeqly+XVvhlKNXeQF5anw==", "dev": true, + "license": "MIT", "dependencies": { "@nx/devkit": "20.0.6", "@nx/js": "20.0.6", @@ -7398,6 +7441,7 @@ "resolved": "https://registry.npmjs.org/@nx/webpack/-/webpack-20.0.6.tgz", "integrity": "sha512-LvjkJ0yVXDCNgxxIKYLMtEJVVdvBVHcB9mgwPdBfl38STAf/HwTuB7XXTZVYu+m9iPusU1VpFpaUlbpQN79f8A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.23.2", "@module-federation/enhanced": "^0.6.0", @@ -7447,6 +7491,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -7462,6 +7507,7 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -7474,6 +7520,7 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -7508,6 +7555,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7524,6 +7572,7 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7533,6 +7582,7 @@ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz", "integrity": "sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "^3.2.7", "glob-parent": "^6.0.1", @@ -7557,6 +7607,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, + "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -7573,6 +7624,7 @@ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.11.0.tgz", "integrity": "sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", @@ -7608,6 +7660,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7617,6 +7670,7 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -7626,6 +7680,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -7687,6 +7742,7 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", "dev": true, + "license": "MIT", "dependencies": { "array-union": "^3.0.1", "dir-glob": "^3.0.1", @@ -7707,6 +7763,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -7716,6 +7773,7 @@ "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -7742,6 +7800,7 @@ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", "dev": true, + "license": "MIT", "dependencies": { "klona": "^2.0.4" }, @@ -7762,6 +7821,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -7776,6 +7836,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "pify": "^4.0.1", @@ -7790,6 +7851,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, + "license": "ISC", "optional": true, "bin": { "semver": "bin/semver" @@ -7810,6 +7872,7 @@ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz", "integrity": "sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==", "dev": true, + "license": "MIT", "dependencies": { "schema-utils": "^4.0.0" }, @@ -7828,13 +7891,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@nx/webpack/node_modules/parse5": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@nx/webpack/node_modules/path-to-regexp": { "version": "0.1.10", @@ -7848,6 +7913,7 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=6" @@ -7858,6 +7924,7 @@ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz", "integrity": "sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==", "dev": true, + "license": "MIT", "dependencies": { "cosmiconfig": "^7.0.0", "klona": "^2.0.5", @@ -7896,6 +7963,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } @@ -7905,6 +7973,7 @@ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", "dev": true, + "license": "MIT", "dependencies": { "klona": "^2.0.4", "neo-async": "^2.6.2" @@ -8001,6 +8070,7 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -8013,6 +8083,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" @@ -8023,6 +8094,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -8035,6 +8107,7 @@ "resolved": "https://registry.npmjs.org/@nx/workspace/-/workspace-20.0.6.tgz", "integrity": "sha512-A7lle47I4JggbhXoUVvkuvULqF0Xejy4LpE0txz9OIM5a9HxW1aIHYYQFuROBuVlMFxAJusPeYFJCCvb+qBxKw==", "dev": true, + "license": "MIT", "dependencies": { "@nx/devkit": "20.0.6", "chalk": "^4.1.0", @@ -8049,6 +8122,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -8064,6 +8138,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8080,6 +8155,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8089,6 +8165,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -8189,7 +8266,6 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", - "license": "MIT", "peer": true, "dependencies": { "tslib": "^2.0.0" @@ -8202,7 +8278,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.5.0.tgz", "integrity": "sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==", - "license": "MIT", "peer": true, "dependencies": { "@peculiar/asn1-schema": "^2.3.8", @@ -8216,10 +8291,9 @@ } }, "node_modules/@peculiar/webcrypto/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", - "license": "0BSD", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "peer": true }, "node_modules/@phenomnomnominal/tsquery": { @@ -8257,6 +8331,7 @@ "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.21.1.tgz", "integrity": "sha512-3n+GgbAZYjaS/k0M03yQsQfR1APbr411r74foknnsGpmhNKBG49VuUkxIU6jORgvJPChoD4WC4PqoHImN1FP0w==", "hasInstallScript": true, + "license": "Apache-2.0", "engines": { "node": ">=16.13" }, @@ -8282,6 +8357,7 @@ "integrity": "sha512-hGVTldUkIkTwoV8//hmnAAiAchi4oMEKD3aW5H2RrnI50tTdwza7VQbTTAyN3OIHWlK5DVg6xV7X8N/9dtOydA==", "devOptional": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { "@prisma/debug": "5.21.1", "@prisma/engines-version": "5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36", @@ -8322,6 +8398,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -8330,6 +8407,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", + "license": "MIT", "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -8343,6 +8421,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -8351,6 +8430,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -8359,6 +8439,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -8367,6 +8448,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -9651,6 +9733,7 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" }, @@ -9666,6 +9749,7 @@ "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dev": true, + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -9690,6 +9774,7 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -9698,13 +9783,15 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@storybook/builder-webpack5/node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dev": true, + "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -10025,6 +10112,7 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" }, @@ -10040,6 +10128,7 @@ "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", "dev": true, + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -10064,6 +10153,7 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -10072,13 +10162,15 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@storybook/core/node_modules/serve-static": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "dev": true, + "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", @@ -10240,9 +10332,9 @@ } }, "node_modules/@stripe/stripe-js": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-3.5.0.tgz", - "integrity": "sha512-pKS3wZnJoL1iTyGBXAvCwduNNeghJHY6QSRSNNvpYnrrQrLZ6Owsazjyynu0e0ObRgks0i7Rv+pe2M7/MBTZpQ==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-4.9.0.tgz", + "integrity": "sha512-tMPZQZZXGWyNX7hbgenq+1xEj2oigJ54XddbtSX36VedoKsPBq7dxwRXu4Xd5FdpT3JDyyDtnmvYkaSnH1yHTQ==", "license": "MIT", "engines": { "node": ">=12.16" @@ -10584,6 +10676,7 @@ "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10.13.0" } @@ -10657,6 +10750,7 @@ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.4.0" } @@ -10743,7 +10837,8 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/@types/cache-manager/-/cache-manager-4.0.6.tgz", "integrity": "sha512-8qL93MF05/xrzFm/LSPtzNEOE1eQF3VwGHAcQEylgp5hDSTe41jtFwbSYAPfyYcVa28y1vYSjIt0c1fLLUiC/Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/cacheable-request": { "version": "6.0.3", @@ -10762,6 +10857,7 @@ "resolved": "https://registry.npmjs.org/@types/color/-/color-3.0.6.tgz", "integrity": "sha512-NMiNcZFRUAiUUCCf7zkAelY8eV3aKqfbzyFQlXpPIEeoNDbsEHGpb854V3gzTsGKYj830I5zPuOwU/TP5/cW6A==", "dev": true, + "license": "MIT", "dependencies": { "@types/color-convert": "*" } @@ -10837,9 +10933,10 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "license": "MIT" }, "node_modules/@types/express": { "version": "5.0.0", @@ -11040,7 +11137,8 @@ "version": "4.17.7", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/mdast": { "version": "3.0.15", @@ -12040,6 +12138,7 @@ "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz", "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "js-yaml": "^3.10.0", "tslib": "^2.4.0" @@ -12053,6 +12152,7 @@ "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.7.tgz", "integrity": "sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -12208,6 +12308,7 @@ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0" } @@ -12812,6 +12913,7 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -13273,6 +13375,7 @@ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "5.1.2" }, @@ -13284,7 +13387,8 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/batch": { "version": "0.6.1", @@ -13612,6 +13716,7 @@ "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", "dev": true, + "license": "(MIT OR Apache-2.0)", "bin": { "btoa": "bin/btoa.js" }, @@ -13670,7 +13775,6 @@ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", "dev": true, - "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" @@ -13680,6 +13784,7 @@ "version": "4.16.2", "resolved": "https://registry.npmjs.org/bull/-/bull-4.16.2.tgz", "integrity": "sha512-VCy33UdPGiIoZHDTrslGXKXWxcIUHNH5Z82pihr8HicbIfAH4SHug1HxlwKEbibVv85hq8rJ9tKAW/cuxv2T0A==", + "license": "MIT", "dependencies": { "cron-parser": "^4.2.1", "get-port": "^5.1.1", @@ -13826,6 +13931,7 @@ "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", "dev": true, + "license": "MIT", "dependencies": { "mime-types": "^2.1.18", "ylru": "^1.2.0" @@ -13838,6 +13944,7 @@ "version": "5.7.6", "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-5.7.6.tgz", "integrity": "sha512-wBxnBHjDxF1RXpHCBD6HGvKER003Ts7IIm0CHpggliHzN1RZditb7rXoduE1rplc2DEFYKxhLKgFuchXMJje9w==", + "license": "MIT", "dependencies": { "eventemitter3": "^5.0.1", "lodash.clonedeep": "^4.5.0", @@ -13871,7 +13978,8 @@ "node_modules/cache-manager/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, "node_modules/cacheable-lookup": { "version": "5.0.4", @@ -13964,6 +14072,7 @@ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.0.0", "caniuse-lite": "^1.0.0", @@ -14691,7 +14800,8 @@ "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/colorette": { "version": "2.0.20", @@ -14966,6 +15076,7 @@ "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", "dev": true, + "license": "MIT", "dependencies": { "depd": "~2.0.0", "keygrip": "~1.1.0" @@ -15091,6 +15202,7 @@ "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4.0" } @@ -15133,9 +15245,9 @@ } }, "node_modules/countries-list": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/countries-list/-/countries-list-3.1.0.tgz", - "integrity": "sha512-HpTBLZba1VPTZSjUnUwR7SykxV7Z/7/+ZM5x5wi5tO99Qvom6bE2SC+AQ18016ujg3jSlYBbMITrHNnPAHSM9Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/countries-list/-/countries-list-3.1.1.tgz", + "integrity": "sha512-nPklKJ5qtmY5MdBKw1NiBAoyx5Sa7p2yPpljZyQ7gyCN1m+eMFs9I6CT37Mxt8zvR5L3VzD3DJBE4WQzX3WF4A==", "license": "MIT" }, "node_modules/countup.js": { @@ -15344,6 +15456,7 @@ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", "dev": true, + "license": "ISC", "engines": { "node": "^14 || ^16 || >=18" }, @@ -15391,6 +15504,7 @@ "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "cssnano": "^6.0.1", @@ -15462,6 +15576,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -15516,6 +15631,7 @@ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", "dev": true, + "license": "MIT", "dependencies": { "cssnano-preset-default": "^6.1.2", "lilconfig": "^3.1.1" @@ -15536,6 +15652,7 @@ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "css-declaration-sorter": "^7.2.0", @@ -15580,6 +15697,7 @@ "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", "dev": true, + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -15592,6 +15710,7 @@ "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, + "license": "MIT", "dependencies": { "css-tree": "~2.2.0" }, @@ -15605,6 +15724,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, + "license": "MIT", "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" @@ -15618,7 +15738,8 @@ "version": "2.0.28", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/cssom": { "version": "0.5.0", @@ -16401,6 +16522,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" @@ -16411,6 +16533,7 @@ "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0" } @@ -16514,7 +16637,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/deep-is": { "version": "0.1.4", @@ -16721,7 +16845,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/delimit-stream": { "version": "0.1.0", @@ -17043,6 +17168,7 @@ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" }, @@ -17170,6 +17296,7 @@ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1" }, @@ -18408,6 +18535,7 @@ "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" }, @@ -18772,6 +18900,7 @@ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" } @@ -18781,6 +18910,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -18856,6 +18986,7 @@ "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" }, @@ -18868,6 +18999,7 @@ "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" }, @@ -18984,6 +19116,7 @@ "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz", "integrity": "sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.16.7", "chalk": "^4.1.2", @@ -19018,6 +19151,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -19034,6 +19168,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } @@ -19043,6 +19178,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -19058,6 +19194,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -19068,6 +19205,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -19084,6 +19222,7 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, + "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -19100,6 +19239,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -19114,6 +19254,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -19122,13 +19263,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -19141,6 +19284,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -19159,6 +19303,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -19217,6 +19362,7 @@ "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", "dev": true, + "license": "MIT", "dependencies": { "js-yaml": "^3.13.1" } @@ -19225,7 +19371,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fs-extra": { "version": "9.1.0", @@ -19380,6 +19527,7 @@ "version": "3.9.0", "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", "engines": { "node": ">= 4" } @@ -19607,6 +19755,7 @@ "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", @@ -19621,6 +19770,7 @@ "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", @@ -19636,13 +19786,15 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "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" }, @@ -20053,6 +20205,7 @@ "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" }, @@ -20253,6 +20406,7 @@ "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", "dev": true, + "license": "MIT", "dependencies": { "deep-equal": "~1.0.1", "http-errors": "~1.8.0" @@ -20266,6 +20420,7 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -20275,6 +20430,7 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, + "license": "MIT", "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", @@ -20291,6 +20447,7 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -20399,6 +20556,7 @@ "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", "dev": true, + "license": "MIT", "dependencies": { "basic-auth": "^2.0.1", "chalk": "^4.1.2", @@ -20426,6 +20584,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -20441,6 +20600,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -20457,6 +20617,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -20466,6 +20627,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -21390,6 +21552,7 @@ "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" } @@ -21445,6 +21608,7 @@ "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", "dev": true, + "license": "MIT", "peerDependencies": { "ws": "*" } @@ -21603,6 +21767,7 @@ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -21621,6 +21786,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -21636,6 +21802,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -21646,6 +21813,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -21662,6 +21830,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -21671,6 +21840,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -21683,6 +21853,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -21846,7 +22017,6 @@ "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", "dev": true, - "license": "MIT", "optional": true, "peer": true, "dependencies": { @@ -21881,7 +22051,6 @@ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, - "license": "MIT", "optional": true, "peer": true, "dependencies": { @@ -23676,7 +23845,6 @@ "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "license": "MIT", "peer": true }, "node_modules/js-sha256": { @@ -24089,6 +24257,7 @@ "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", "dev": true, + "license": "MIT", "dependencies": { "tsscmp": "1.0.6" }, @@ -24135,6 +24304,7 @@ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -24144,6 +24314,7 @@ "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.3.tgz", "integrity": "sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "^1.3.5", "cache-content-type": "^1.0.0", @@ -24177,13 +24348,15 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/koa-convert": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", "dev": true, + "license": "MIT", "dependencies": { "co": "^4.6.0", "koa-compose": "^4.1.0" @@ -24197,6 +24370,7 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, + "license": "MIT", "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", @@ -24223,6 +24397,7 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -24402,6 +24577,7 @@ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, + "license": "MIT", "engines": { "node": ">=14" }, @@ -24414,6 +24590,7 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.3.tgz", "integrity": "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } @@ -24994,13 +25171,15 @@ "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" }, "node_modules/lodash.clonedeepwith": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz", "integrity": "sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -25045,7 +25224,8 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", @@ -25263,6 +25443,7 @@ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", "dev": true, + "license": "Apache-2.0", "dependencies": { "date-format": "^4.0.14", "debug": "^4.3.4", @@ -25278,7 +25459,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -25503,7 +25685,8 @@ "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "dev": true, + "license": "CC0-1.0" }, "node_modules/media-typer": { "version": "0.3.0", @@ -26404,10 +26587,9 @@ } }, "node_modules/msgpackr": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.0.tgz", - "integrity": "sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==", - "license": "MIT", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz", + "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==", "optionalDependencies": { "msgpackr-extract": "^3.0.2" } @@ -26674,23 +26856,22 @@ } }, "node_modules/ngx-skeleton-loader": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ngx-skeleton-loader/-/ngx-skeleton-loader-7.0.0.tgz", - "integrity": "sha512-myc6GNcNhyksZrimIFkCxeihi0kQ8JhQVZiGbtiIv4gYrnnRk5nXbs3kYitK8E8OstHG+jlsmRofqGBxuIsYTA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ngx-skeleton-loader/-/ngx-skeleton-loader-9.0.0.tgz", + "integrity": "sha512-aO4/V6oGdZGNcTjasTg/fwzJJYl/ZmNKgCukOEQdUK3GSFOZtB/3GGULMJuZ939hk3Hzqh1OBiLfIM1SqTfhqg==", "license": "MIT", "dependencies": { - "perf-marks": "^1.13.4", "tslib": "^2.0.0" }, "peerDependencies": { - "@angular/common": ">=8.0.0", - "@angular/core": ">=8.0.0" + "@angular/common": ">=16.0.0", + "@angular/core": ">=16.0.0" } }, "node_modules/ngx-stripe": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/ngx-stripe/-/ngx-stripe-18.0.0.tgz", - "integrity": "sha512-AT67vLeqEUDMnK5TfEaorumYJyOWqecbrh/1UWNtN8vF6Yzb0L/Dty3ANAa/QQi0OvBg6gXrudrhEnT8pT5lng==", + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/ngx-stripe/-/ngx-stripe-18.1.0.tgz", + "integrity": "sha512-fNWmFaCWWzfsr8GU9Bmi6fwgHZHMI9UwpV5M0HMvkANnz9n7JWjP2Uck6zk0lXdu9q989aIbqj4awbLCZk/TUw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -26698,7 +26879,7 @@ "peerDependencies": { "@angular/common": ">=18.0.0 <19.0.0", "@angular/core": ">=18.0.0 <19.0.0", - "@stripe/stripe-js": ">=3.0.0 <4.0.0" + "@stripe/stripe-js": ">=4.0.0 <5.0.0" } }, "node_modules/nice-napi": { @@ -26923,7 +27104,8 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-releases": { "version": "2.0.18", @@ -26936,6 +27118,7 @@ "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", @@ -27161,6 +27344,7 @@ "integrity": "sha512-z8PMPEXxtADwxsNXamZdDbx65fcNcR4gTmX7N94GKmpZNrjwd3m7RcnoYgQp5vA8kFQkMR+320mtq5NkGJPZvg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", @@ -27229,6 +27413,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -27244,6 +27429,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -27260,6 +27446,7 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -27272,6 +27459,7 @@ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -27281,6 +27469,7 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -27293,6 +27482,7 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dotenv": "^16.4.4" }, @@ -27308,6 +27498,7 @@ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" }, @@ -27323,6 +27514,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -27332,6 +27524,7 @@ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, + "license": "MIT", "bin": { "is-docker": "cli.js" }, @@ -27347,6 +27540,7 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, + "license": "MIT", "dependencies": { "is-docker": "^2.0.0" }, @@ -27358,13 +27552,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/nx/node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", "dev": true, + "license": "MIT", "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -27382,6 +27578,7 @@ "resolved": "https://registry.npmjs.org/ora/-/ora-5.3.0.tgz", "integrity": "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==", "dev": true, + "license": "MIT", "dependencies": { "bl": "^4.0.3", "chalk": "^4.1.0", @@ -27404,6 +27601,7 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, + "license": "MIT", "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -27416,13 +27614,15 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/nx/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -27974,6 +28174,7 @@ "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" } @@ -28219,18 +28420,6 @@ "dev": true, "license": "MIT" }, - "node_modules/perf-marks": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/perf-marks/-/perf-marks-1.14.2.tgz", - "integrity": "sha512-N0/bQcuTlETpgox/DsXS1voGjqaoamMoiyhncgeW3rSHy/qw8URVgmPRYfFDQns/+C6yFUHDbeSBGL7ixT6Y4A==", - "license": "MIT", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -28382,7 +28571,6 @@ "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", - "license": "MIT", "peer": true, "funding": { "type": "opencollective", @@ -28394,6 +28582,7 @@ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==", "dev": true, + "license": "MIT", "dependencies": { "async": "^2.6.4", "debug": "^3.2.7", @@ -28408,6 +28597,7 @@ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.14" } @@ -28417,6 +28607,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -28425,7 +28616,8 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/possible-typed-array-names": { "version": "1.0.0", @@ -28470,6 +28662,7 @@ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" @@ -28486,6 +28679,7 @@ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", @@ -28504,6 +28698,7 @@ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" @@ -28520,6 +28715,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", "dev": true, + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -28532,6 +28728,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", "dev": true, + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -28544,6 +28741,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", "dev": true, + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -28556,6 +28754,7 @@ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", "dev": true, + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -28568,6 +28767,7 @@ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -28660,6 +28860,7 @@ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", "stylehacks": "^6.1.1" @@ -28676,6 +28877,7 @@ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", @@ -28694,6 +28896,7 @@ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -28709,6 +28912,7 @@ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", "dev": true, + "license": "MIT", "dependencies": { "colord": "^2.9.3", "cssnano-utils": "^4.0.2", @@ -28726,6 +28930,7 @@ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "cssnano-utils": "^4.0.2", @@ -28743,6 +28948,7 @@ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.16" }, @@ -28817,6 +29023,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", "dev": true, + "license": "MIT", "engines": { "node": "^14 || ^16 || >=18.0" }, @@ -28829,6 +29036,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -28844,6 +29052,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -28859,6 +29068,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -28874,6 +29084,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -28889,6 +29100,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -28904,6 +29116,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" @@ -28920,6 +29133,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -28935,6 +29149,7 @@ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -28950,6 +29165,7 @@ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", "dev": true, + "license": "MIT", "dependencies": { "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" @@ -28966,6 +29182,7 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "caniuse-api": "^3.0.0" @@ -28982,6 +29199,7 @@ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, @@ -29010,6 +29228,7 @@ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", "dev": true, + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", "svgo": "^3.2.0" @@ -29026,6 +29245,7 @@ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", "dev": true, + "license": "MIT", "dependencies": { "postcss-selector-parser": "^6.0.16" }, @@ -29139,6 +29359,7 @@ "integrity": "sha512-PB+Iqzld/uQBPaaw2UVIk84kb0ITsLajzsxzsadxxl54eaU5Gyl2/L02ysivHxK89t7YrfQJm+Ggk37uvM70oQ==", "devOptional": true, "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { "@prisma/engines": "5.21.1" }, @@ -29192,6 +29413,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/promise-coalesce/-/promise-coalesce-1.1.2.tgz", "integrity": "sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg==", + "license": "BSD-3-Clause", "engines": { "node": ">=16" } @@ -29257,7 +29479,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/prr": { "version": "1.0.1", @@ -29482,6 +29705,7 @@ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, + "license": "MIT", "dependencies": { "pify": "^2.3.0" } @@ -29591,6 +29815,10 @@ "version": "4.7.0", "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", "integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.0", @@ -30089,6 +30317,7 @@ "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" @@ -30257,7 +30486,7 @@ "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.5" }, "bin": { "rollup": "dist/bin/rollup" @@ -30539,7 +30768,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/select": { "version": "1.1.2", @@ -31038,7 +31268,8 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/source-map": { "version": "0.7.4", @@ -31408,6 +31639,7 @@ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", "dev": true, + "license": "MIT", "dependencies": { "date-format": "^4.0.14", "debug": "^4.3.4", @@ -31422,6 +31654,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -31436,6 +31669,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -31445,6 +31679,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -31632,9 +31867,9 @@ } }, "node_modules/stripe": { - "version": "15.11.0", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-15.11.0.tgz", - "integrity": "sha512-qmZF0PN1jRVpiQrXL8eTb9Jy/6S+aUlcDquKBFT2h3PkaD7RZ444FIojVXUg67FK2zFIUNXgMv02c7csdL5qHg==", + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-17.3.0.tgz", + "integrity": "sha512-WACmytj1MssbIwGwPfAomo61jgldb2B/cB6A3W/Bqs9zId1olVcAa8X7HERkqpw4190GSsbvrD7KnkZogatyvw==", "license": "MIT", "dependencies": { "@types/node": ">=8.1.0", @@ -31666,6 +31901,7 @@ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "postcss-selector-parser": "^6.0.16" @@ -31712,6 +31948,7 @@ "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.1.3.tgz", "integrity": "sha512-TY0SKwiY7D2kMd3UxaWKSf3xHF0FFN/FAfsSqfrhxRT/koXTwffq2cgEWDkLQz7VojMu7qEEHt5TlMjkPx9UDw==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "^3.2.12", "normalize-path": "^3.0.0" @@ -31779,6 +32016,7 @@ "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", "dev": true, + "license": "MIT", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", @@ -31804,6 +32042,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } @@ -31857,6 +32096,7 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, + "license": "MIT", "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -32384,6 +32624,7 @@ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", @@ -32404,6 +32645,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -32419,6 +32661,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -32435,6 +32678,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -32444,6 +32688,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -32514,6 +32759,7 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.0.tgz", "integrity": "sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", @@ -32528,6 +32774,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -32543,6 +32790,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -32559,6 +32807,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -32568,6 +32817,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -32592,11 +32842,11 @@ "license": "0BSD" }, "node_modules/tslint": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", - "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", "dev": true, - "license": "Apache-2.0", "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", @@ -32607,10 +32857,10 @@ "glob": "^7.1.1", "js-yaml": "^3.13.1", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", + "mkdirp": "^0.5.3", "resolve": "^1.3.2", "semver": "^5.3.0", - "tslib": "^1.8.0", + "tslib": "^1.13.0", "tsutils": "^2.29.0" }, "bin": { @@ -32620,7 +32870,7 @@ "node": ">=4.8.0" }, "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev" + "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" } }, "node_modules/tslint/node_modules/brace-expansion": { @@ -32628,7 +32878,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^1.0.0", @@ -32640,7 +32889,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "peer": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -32654,7 +32902,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "license": "ISC", "peer": true, "bin": { "semver": "bin/semver" @@ -32665,7 +32912,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, - "license": "0BSD", "peer": true }, "node_modules/tsscmp": { @@ -32673,6 +32919,7 @@ "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.x" } @@ -32682,7 +32929,6 @@ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "tslib": "^1.8.1" @@ -32696,7 +32942,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, - "license": "0BSD", "peer": true }, "node_modules/tuf-js": { @@ -33162,6 +33407,7 @@ "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", "dev": true, + "license": "MIT", "engines": { "node": ">=4", "yarn": "*" @@ -33224,7 +33470,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/url-parse": { "version": "1.5.10", @@ -34007,7 +34254,6 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.8.1.tgz", "integrity": "sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A==", - "license": "MIT", "peer": true, "dependencies": { "@peculiar/asn1-schema": "^2.3.13", @@ -34018,10 +34264,9 @@ } }, "node_modules/webcrypto-core/node_modules/tslib": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", - "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", - "license": "0BSD", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "peer": true }, "node_modules/webidl-conversions": { @@ -34085,6 +34330,7 @@ "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", "dev": true, + "license": "MIT", "dependencies": { "@discoveryjs/json-ext": "0.5.7", "acorn": "^8.0.4", @@ -34397,6 +34643,7 @@ "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -34921,6 +35168,7 @@ "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -34962,7 +35210,8 @@ "node_modules/zone.js": { "version": "0.14.10", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", - "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==" + "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==", + "license": "MIT" } } } diff --git a/package.json b/package.json index e0ca4b6a8..909859788 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.119.0", + "version": "2.122.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", @@ -90,7 +90,7 @@ "@prisma/client": "5.21.1", "@simplewebauthn/browser": "9.0.1", "@simplewebauthn/server": "9.0.3", - "@stripe/stripe-js": "3.5.0", + "@stripe/stripe-js": "4.9.0", "alphavantage": "2.2.0", "await-lock": "^2.2.2", "big.js": "6.2.1", @@ -109,7 +109,7 @@ "class-validator": "0.14.1", "color": "4.2.3", "countries-and-timezones": "3.4.1", - "countries-list": "3.1.0", + "countries-list": "3.1.1", "countup.js": "2.8.0", "date-fns": "3.6.0", "envalid": "7.3.1", @@ -125,8 +125,8 @@ "ng-extract-i18n-merge": "2.12.0", "ngx-device-detector": "8.0.0", "ngx-markdown": "18.0.0", - "ngx-skeleton-loader": "7.0.0", - "ngx-stripe": "18.0.0", + "ngx-skeleton-loader": "9.0.0", + "ngx-stripe": "18.1.0", "open-color": "1.9.1", "papaparse": "5.3.1", "passport": "0.7.0", @@ -134,7 +134,7 @@ "passport-jwt": "4.0.1", "reflect-metadata": "0.1.13", "rxjs": "7.5.6", - "stripe": "15.11.0", + "stripe": "17.3.0", "svgmap": "2.6.0", "twitter-api-v2": "1.14.2", "uuid": "9.0.1", diff --git a/prisma/migrations/20241029190323_added_data_provider_ghostfolio_daily_requests_to_analytics/migration.sql b/prisma/migrations/20241029190323_added_data_provider_ghostfolio_daily_requests_to_analytics/migration.sql new file mode 100644 index 000000000..a9566dd82 --- /dev/null +++ b/prisma/migrations/20241029190323_added_data_provider_ghostfolio_daily_requests_to_analytics/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Analytics" ADD COLUMN "dataProviderGhostfolioDailyRequests" INTEGER NOT NULL DEFAULT 0; diff --git a/prisma/migrations/20241102121004_added_last_request_at_to_analytics/migration.sql b/prisma/migrations/20241102121004_added_last_request_at_to_analytics/migration.sql new file mode 100644 index 000000000..b7af31037 --- /dev/null +++ b/prisma/migrations/20241102121004_added_last_request_at_to_analytics/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "Analytics" ADD COLUMN "lastRequestAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; + +-- CreateIndex +CREATE INDEX "Analytics_lastRequestAt_idx" ON "Analytics"("lastRequestAt"); diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0e33e845f..af58da460 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -65,12 +65,15 @@ model AccountBalance { } model Analytics { - activityCount Int @default(0) - country String? - updatedAt DateTime @updatedAt - userId String @id - User User @relation(fields: [userId], onDelete: Cascade, references: [id]) - + activityCount Int @default(0) + country String? + dataProviderGhostfolioDailyRequests Int @default(0) + lastRequestAt DateTime @default(now()) + updatedAt DateTime @updatedAt + userId String @id + User User @relation(fields: [userId], onDelete: Cascade, references: [id]) + + @@index([lastRequestAt]) @@index([updatedAt]) } diff --git a/test/import/ok-derived-currency.json b/test/import/ok-derived-currency.json index b43be395d..fe5554beb 100644 --- a/test/import/ok-derived-currency.json +++ b/test/import/ok-derived-currency.json @@ -23,7 +23,7 @@ "unitPrice": 10875.00, "currency": "ZAc", "dataSource": "YAHOO", - "date": "2024-06-27T22:00:00.000Z", + "date": "2024-06-28T00:00:00.000Z", "symbol": "JSE.JO" } ] diff --git a/test/import/ok-vti-buy-long-history.json b/test/import/ok-vti-buy-long-history.json index e6020e405..1586cff7b 100644 --- a/test/import/ok-vti-buy-long-history.json +++ b/test/import/ok-vti-buy-long-history.json @@ -11,7 +11,7 @@ "unitPrice": 65.31, "currency": "USD", "dataSource": "YAHOO", - "date": "2012-01-02T22:00:00.000Z", + "date": "2012-01-03T00:00:00.000Z", "symbol": "VTI" }, { @@ -21,7 +21,7 @@ "unitPrice": 65.40, "currency": "USD", "dataSource": "YAHOO", - "date": "2011-01-02T22:00:00.000Z", + "date": "2011-01-03T00:00:00.000Z", "symbol": "VTI" }, { @@ -31,7 +31,7 @@ "unitPrice": 57.05, "currency": "USD", "dataSource": "YAHOO", - "date": "2010-01-03T22:00:00.000Z", + "date": "2010-01-04T00:00:00.000Z", "symbol": "VTI" } ] diff --git a/test/import/ok-without-accounts.json b/test/import/ok-without-accounts.json index 63961be74..2ba0925b1 100644 --- a/test/import/ok-without-accounts.json +++ b/test/import/ok-without-accounts.json @@ -11,7 +11,7 @@ "unitPrice": 0, "currency": "USD", "dataSource": "YAHOO", - "date": "2050-06-05T22:00:00.000Z", + "date": "2050-06-06T00:00:00.000Z", "symbol": "MSFT" }, { @@ -21,7 +21,7 @@ "unitPrice": 500000, "currency": "USD", "dataSource": "MANUAL", - "date": "2021-12-31T22:00:00.000Z", + "date": "2022-01-01T00:00:00.000Z", "symbol": "Penthouse Apartment" }, { @@ -31,7 +31,7 @@ "unitPrice": 0.62, "currency": "USD", "dataSource": "YAHOO", - "date": "2021-11-16T22:00:00.000Z", + "date": "2021-11-17T00:00:00.000Z", "symbol": "MSFT" }, { @@ -41,7 +41,7 @@ "unitPrice": 298.58, "currency": "USD", "dataSource": "YAHOO", - "date": "2021-09-15T22:00:00.000Z", + "date": "2021-09-16T00:00:00.000Z", "symbol": "MSFT" } ] diff --git a/test/import/ok.json b/test/import/ok.json index 0af285500..4bce98ba2 100644 --- a/test/import/ok.json +++ b/test/import/ok.json @@ -23,7 +23,7 @@ "unitPrice": 0, "currency": "USD", "dataSource": "YAHOO", - "date": "2050-06-05T22:00:00.000Z", + "date": "2050-06-06T00:00:00.000Z", "symbol": "US5949181045" }, { @@ -35,7 +35,7 @@ "unitPrice": 500000, "currency": "USD", "dataSource": "MANUAL", - "date": "2021-12-31T22:00:00.000Z", + "date": "2022-01-01T00:00:00.000Z", "symbol": "Penthouse Apartment" }, { @@ -47,7 +47,7 @@ "unitPrice": 0.62, "currency": "USD", "dataSource": "YAHOO", - "date": "2021-11-16T22:00:00.000Z", + "date": "2021-11-17T00:00:00.000Z", "symbol": "MSFT" }, { @@ -59,7 +59,7 @@ "unitPrice": 298.58, "currency": "USD", "dataSource": "YAHOO", - "date": "2021-09-15T22:00:00.000Z", + "date": "2021-09-16T00:00:00.000Z", "symbol": "MSFT" }, { @@ -71,7 +71,7 @@ "unitPrice": 0, "currency": "USD", "dataSource": "MANUAL", - "date": "2021-08-31T22:00:00.000Z", + "date": "2021-09-01T00:00:00.000Z", "symbol": "Account Opening Fee" } ] diff --git a/test/import/unavailable-exchange-rate.json b/test/import/unavailable-exchange-rate.json index 2d21a76c3..4d8be156a 100644 --- a/test/import/unavailable-exchange-rate.json +++ b/test/import/unavailable-exchange-rate.json @@ -12,7 +12,7 @@ "unitPrice": 0, "currency": "EUR", "dataSource": "YAHOO", - "date": "1990-01-01T22:00:00.000Z", + "date": "1990-01-01T00:00:00.000Z", "symbol": "MSFT" } ]