diff --git a/.config/prisma.ts b/.config/prisma.ts index 64691136c..a9e7bbf64 100644 --- a/.config/prisma.ts +++ b/.config/prisma.ts @@ -6,6 +6,9 @@ import { join } from 'node:path'; expand(config({ quiet: true })); export default defineConfig({ + datasource: { + url: process.env.DATABASE_URL ?? '' + }, migrations: { path: join(__dirname, '..', 'prisma', 'migrations'), seed: `node ${join(__dirname, '..', 'prisma', 'seed.mts')}` diff --git a/.env.dev b/.env.dev index d0c9a1576..b7ad632f8 100644 --- a/.env.dev +++ b/.env.dev @@ -12,7 +12,7 @@ POSTGRES_PASSWORD= # VARIOUS ACCESS_TOKEN_SALT= -DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?connect_timeout=300&sslmode=prefer +DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?connect_timeout=300 JWT_SECRET_KEY= # DEVELOPMENT diff --git a/.env.example b/.env.example index e4a935626..1f43bfd45 100644 --- a/.env.example +++ b/.env.example @@ -12,5 +12,5 @@ POSTGRES_PASSWORD= # VARIOUS ACCESS_TOKEN_SALT= -DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?connect_timeout=300&sslmode=prefer +DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?connect_timeout=300 JWT_SECRET_KEY= diff --git a/CHANGELOG.md b/CHANGELOG.md index e559395e4..1fc82ec50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,16 +5,27 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 3.0.0 - 2026-04-23 + +### Added + +- Added a blog post: _Announcing Ghostfolio 3.0_ ### Changed +- Migrated from _Material Design_ 2 to _Material Design_ 3 +- Moved the total amount, change and performance with currency effects on the analysis page from experimental to general availability - Refreshed the cryptocurrencies list - Upgraded `countup.js` from version `2.9.0` to `2.10.0` - Upgraded `jsonpath` from version `1.2.1` to `1.3.0` - Upgraded `nestjs` from version `11.1.14` to `11.1.19` - Upgraded `ngx-markdown` from version `21.1.0` to `21.2.0` - Upgraded `Nx` from version `22.6.4` to `22.6.5` +- Upgraded `prisma` from version `6.19.0` to `7.7.0` + +### Todo + +- **Breaking Change**: The `sslmode=prefer` parameter in `DATABASE_URL` is no longer supported. Please update your environment variables (see `.env`) to use `sslmode=require` if _SSL_ is enabled or remove the `sslmode` parameter entirely if _SSL_ is not used. ## 2.255.0 - 2026-04-20 diff --git a/README.md b/README.md index d019721c1..87710d610 100644 --- a/README.md +++ b/README.md @@ -85,26 +85,26 @@ We provide official container images hosted on [Docker Hub](https://hub.docker.c ### Supported Environment Variables -| Name | Type | Default Value | Description | -| --------------------------- | --------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| `ACCESS_TOKEN_SALT` | `string` | | A random string used as salt for access tokens | -| `API_KEY_COINGECKO_DEMO` | `string` (optional) |   | The _CoinGecko_ Demo API key | -| `API_KEY_COINGECKO_PRO` | `string` (optional) | | The _CoinGecko_ Pro API key | -| `DATABASE_URL` | `string` | | The database connection URL, e.g. `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}?sslmode=prefer` | -| `ENABLE_FEATURE_AUTH_TOKEN` | `boolean` (optional) | `true` | Enables authentication via security token | -| `HOST` | `string` (optional) | `0.0.0.0` | The host where the Ghostfolio application will run on | -| `JWT_SECRET_KEY` | `string` | | A random string used for _JSON Web Tokens_ (JWT) | -| `LOG_LEVELS` | `string[]` (optional) | | The logging levels for the Ghostfolio application, e.g. `["debug","error","log","warn"]` | -| `PORT` | `number` (optional) | `3333` | The port where the Ghostfolio application will run on | -| `POSTGRES_DB` | `string` | | The name of the _PostgreSQL_ database | -| `POSTGRES_PASSWORD` | `string` | | The password of the _PostgreSQL_ database | -| `POSTGRES_USER` | `string` | | The user of the _PostgreSQL_ database | -| `REDIS_DB` | `number` (optional) | `0` | The database index of _Redis_ | -| `REDIS_HOST` | `string` | | The host where _Redis_ is running | -| `REDIS_PASSWORD` | `string` | | The password of _Redis_ | -| `REDIS_PORT` | `number` | | The port where _Redis_ is running | -| `REQUEST_TIMEOUT` | `number` (optional) | `2000` | The timeout of network requests to data providers in milliseconds | -| `ROOT_URL` | `string` (optional) | `http://0.0.0.0:3333` | The root URL of the Ghostfolio application, used for generating callback URLs and external links. | +| Name | Type | Default Value | Description | +| --------------------------- | --------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------- | +| `ACCESS_TOKEN_SALT` | `string` | | A random string used as salt for access tokens | +| `API_KEY_COINGECKO_DEMO` | `string` (optional) |   | The _CoinGecko_ Demo API key | +| `API_KEY_COINGECKO_PRO` | `string` (optional) | | The _CoinGecko_ Pro API key | +| `DATABASE_URL` | `string` | | The database connection URL, e.g. `postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}` | +| `ENABLE_FEATURE_AUTH_TOKEN` | `boolean` (optional) | `true` | Enables authentication via security token | +| `HOST` | `string` (optional) | `0.0.0.0` | The host where the Ghostfolio application will run on | +| `JWT_SECRET_KEY` | `string` | | A random string used for _JSON Web Tokens_ (JWT) | +| `LOG_LEVELS` | `string[]` (optional) | | The logging levels for the Ghostfolio application, e.g. `["debug","error","log","warn"]` | +| `PORT` | `number` (optional) | `3333` | The port where the Ghostfolio application will run on | +| `POSTGRES_DB` | `string` | | The name of the _PostgreSQL_ database | +| `POSTGRES_PASSWORD` | `string` | | The password of the _PostgreSQL_ database | +| `POSTGRES_USER` | `string` | | The user of the _PostgreSQL_ database | +| `REDIS_DB` | `number` (optional) | `0` | The database index of _Redis_ | +| `REDIS_HOST` | `string` | | The host where _Redis_ is running | +| `REDIS_PASSWORD` | `string` | | The password of _Redis_ | +| `REDIS_PORT` | `number` | | The port where _Redis_ is running | +| `REQUEST_TIMEOUT` | `number` (optional) | `2000` | The timeout of network requests to data providers in milliseconds | +| `ROOT_URL` | `string` (optional) | `http://0.0.0.0:3333` | The root URL of the Ghostfolio application, used for generating callback URLs and external links. | #### OpenID Connect OIDC (Experimental) diff --git a/apps/api/src/app/admin/admin.service.ts b/apps/api/src/app/admin/admin.service.ts index 27abb6254..ba338b5b9 100644 --- a/apps/api/src/app/admin/admin.service.ts +++ b/apps/api/src/app/admin/admin.service.ts @@ -36,7 +36,6 @@ import { BadRequestException, HttpException, Injectable, - Logger, NotFoundException } from '@nestjs/common'; import { @@ -44,7 +43,6 @@ import { AssetSubClass, DataSource, Prisma, - PrismaClient, Property, SymbolProfile } from '@prisma/client'; @@ -280,186 +278,178 @@ export class AdminService { const extendedPrismaClient = this.getExtendedPrismaClient(); - try { - const symbolProfileResult = await Promise.all([ - extendedPrismaClient.symbolProfile.findMany({ - skip, - take, - where, - orderBy: [...orderBy, { id: sortDirection }], - select: { - _count: { - select: { - activities: true, - watchedBy: true - } - }, - activities: { - orderBy: [{ date: 'asc' }], - select: { date: true }, - take: 1 - }, - assetClass: true, - assetSubClass: true, - comment: true, - countries: true, - currency: true, - dataSource: true, - id: true, - isActive: true, - isUsedByUsersWithSubscription: true, - name: true, - scraperConfiguration: true, - sectors: true, - symbol: true, - SymbolProfileOverrides: true - } - }), - this.prismaService.symbolProfile.count({ where }) - ]); - const assetProfiles = symbolProfileResult[0]; - let count = symbolProfileResult[1]; - - const lastMarketPrices = await this.prismaService.marketData.findMany({ - distinct: ['dataSource', 'symbol'], - orderBy: { date: 'desc' }, + const symbolProfileResult = await Promise.all([ + extendedPrismaClient.symbolProfile.findMany({ + skip, + take, + where, + orderBy: [...orderBy, { id: sortDirection }], select: { + _count: { + select: { + activities: true, + watchedBy: true + } + }, + activities: { + orderBy: [{ date: 'asc' }], + select: { date: true }, + take: 1 + }, + assetClass: true, + assetSubClass: true, + comment: true, + countries: true, + currency: true, dataSource: true, - marketPrice: true, - symbol: true + id: true, + isActive: true, + isUsedByUsersWithSubscription: true, + name: true, + scraperConfiguration: true, + sectors: true, + symbol: true, + SymbolProfileOverrides: true + } + }), + this.prismaService.symbolProfile.count({ where }) + ]); + const assetProfiles = symbolProfileResult[0]; + let count = symbolProfileResult[1]; + + const lastMarketPrices = await this.prismaService.marketData.findMany({ + distinct: ['dataSource', 'symbol'], + orderBy: { date: 'desc' }, + select: { + dataSource: true, + marketPrice: true, + symbol: true + }, + where: { + dataSource: { + in: assetProfiles.map(({ dataSource }) => { + return dataSource; + }) }, - where: { - dataSource: { - in: assetProfiles.map(({ dataSource }) => { - return dataSource; - }) - }, - symbol: { - in: assetProfiles.map(({ symbol }) => { - return symbol; - }) - } + symbol: { + in: assetProfiles.map(({ symbol }) => { + return symbol; + }) } - }); + } + }); - const lastMarketPriceMap = new Map(); + const lastMarketPriceMap = new Map(); - for (const { dataSource, marketPrice, symbol } of lastMarketPrices) { - lastMarketPriceMap.set( - getAssetProfileIdentifier({ dataSource, symbol }), - marketPrice - ); - } + for (const { dataSource, marketPrice, symbol } of lastMarketPrices) { + lastMarketPriceMap.set( + getAssetProfileIdentifier({ dataSource, symbol }), + marketPrice + ); + } + + let marketData: AdminMarketDataItem[] = await Promise.all( + assetProfiles.map( + async ({ + _count, + activities, + assetClass, + assetSubClass, + comment, + countries, + currency, + dataSource, + id, + isActive, + isUsedByUsersWithSubscription, + name, + sectors, + symbol, + SymbolProfileOverrides + }) => { + let countriesCount = countries ? Object.keys(countries).length : 0; + + const lastMarketPrice = lastMarketPriceMap.get( + getAssetProfileIdentifier({ dataSource, symbol }) + ); + + const marketDataItemCount = + marketDataItems.find((marketDataItem) => { + return ( + marketDataItem.dataSource === dataSource && + marketDataItem.symbol === symbol + ); + })?._count ?? 0; + + let sectorsCount = sectors ? Object.keys(sectors).length : 0; + + if (SymbolProfileOverrides) { + assetClass = SymbolProfileOverrides.assetClass ?? assetClass; + assetSubClass = + SymbolProfileOverrides.assetSubClass ?? assetSubClass; + + if ( + (SymbolProfileOverrides.countries as unknown as Prisma.JsonArray) + ?.length > 0 + ) { + countriesCount = ( + SymbolProfileOverrides.countries as unknown as Prisma.JsonArray + ).length; + } - let marketData: AdminMarketDataItem[] = await Promise.all( - assetProfiles.map( - async ({ - _count, - activities, + name = SymbolProfileOverrides.name ?? name; + + if ( + (SymbolProfileOverrides.sectors as unknown as Sector[])?.length > + 0 + ) { + sectorsCount = ( + SymbolProfileOverrides.sectors as unknown as Prisma.JsonArray + ).length; + } + } + + return { assetClass, assetSubClass, comment, - countries, + countriesCount, currency, dataSource, id, isActive, - isUsedByUsersWithSubscription, + lastMarketPrice, + marketDataItemCount, name, - sectors, + sectorsCount, symbol, - SymbolProfileOverrides - }) => { - let countriesCount = countries ? Object.keys(countries).length : 0; - - const lastMarketPrice = lastMarketPriceMap.get( - getAssetProfileIdentifier({ dataSource, symbol }) - ); - - const marketDataItemCount = - marketDataItems.find((marketDataItem) => { - return ( - marketDataItem.dataSource === dataSource && - marketDataItem.symbol === symbol - ); - })?._count ?? 0; - - let sectorsCount = sectors ? Object.keys(sectors).length : 0; - - if (SymbolProfileOverrides) { - assetClass = SymbolProfileOverrides.assetClass ?? assetClass; - assetSubClass = - SymbolProfileOverrides.assetSubClass ?? assetSubClass; - - if ( - ( - SymbolProfileOverrides.countries as unknown as Prisma.JsonArray - )?.length > 0 - ) { - countriesCount = ( - SymbolProfileOverrides.countries as unknown as Prisma.JsonArray - ).length; - } - - name = SymbolProfileOverrides.name ?? name; - - if ( - (SymbolProfileOverrides.sectors as unknown as Sector[]) - ?.length > 0 - ) { - sectorsCount = ( - SymbolProfileOverrides.sectors as unknown as Prisma.JsonArray - ).length; - } - } - - return { - assetClass, - assetSubClass, - comment, - currency, - countriesCount, - dataSource, - id, - isActive, - lastMarketPrice, - name, - symbol, - marketDataItemCount, - sectorsCount, - activitiesCount: _count.activities, - date: activities?.[0]?.date, - isUsedByUsersWithSubscription: - await isUsedByUsersWithSubscription, - watchedByCount: _count.watchedBy - }; - } - ) - ); - - if (presetId) { - if (presetId === 'ETF_WITHOUT_COUNTRIES') { - marketData = marketData.filter(({ countriesCount }) => { - return countriesCount === 0; - }); - } else if (presetId === 'ETF_WITHOUT_SECTORS') { - marketData = marketData.filter(({ sectorsCount }) => { - return sectorsCount === 0; - }); + activitiesCount: _count.activities, + date: activities?.[0]?.date, + isUsedByUsersWithSubscription: await isUsedByUsersWithSubscription, + watchedByCount: _count.watchedBy + }; } + ) + ); - count = marketData.length; + if (presetId) { + if (presetId === 'ETF_WITHOUT_COUNTRIES') { + marketData = marketData.filter(({ countriesCount }) => { + return countriesCount === 0; + }); + } else if (presetId === 'ETF_WITHOUT_SECTORS') { + marketData = marketData.filter(({ sectorsCount }) => { + return sectorsCount === 0; + }); } - return { - count, - marketData - }; - } finally { - await extendedPrismaClient.$disconnect(); - - Logger.debug('Disconnect extended prisma client', 'AdminService'); + count = marketData.length; } + + return { + count, + marketData + }; } public async getMarketDataBySymbol({ @@ -704,8 +694,6 @@ export class AdminService { } private getExtendedPrismaClient() { - Logger.debug('Connect extended prisma client', 'AdminService'); - const symbolProfileExtension = Prisma.defineExtension((client) => { return client.$extends({ result: { @@ -746,7 +734,7 @@ export class AdminService { }); }); - return new PrismaClient().$extends(symbolProfileExtension); + return this.prismaService.$extends(symbolProfileExtension); } private async getMarketDataForCurrencies(): Promise { diff --git a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts index e7e05330f..cf4b5052f 100644 --- a/apps/api/src/app/endpoints/sitemap/sitemap.service.ts +++ b/apps/api/src/app/endpoints/sitemap/sitemap.service.ts @@ -120,6 +120,10 @@ export class SitemapService { { languageCode: 'en', routerLink: ['2025', '11', 'black-weeks-2025'] + }, + { + languageCode: 'en', + routerLink: ['2026', '04', 'ghostfolio-3'] } ] .map(({ languageCode, routerLink }) => { diff --git a/apps/api/src/middlewares/html-template.middleware.ts b/apps/api/src/middlewares/html-template.middleware.ts index c958718f6..2b8820e81 100644 --- a/apps/api/src/middlewares/html-template.middleware.ts +++ b/apps/api/src/middlewares/html-template.middleware.ts @@ -83,6 +83,10 @@ const locales = { '/en/blog/2025/11/black-weeks-2025': { featureGraphicPath: 'assets/images/blog/black-weeks-2025.jpg', title: `Black Weeks 2025 - ${title}` + }, + '/en/blog/2026/04/ghostfolio-3': { + featureGraphicPath: 'assets/images/blog/ghostfolio-3.jpg', + title: `Announcing Ghostfolio 3.0 - ${title}` } }; diff --git a/apps/api/src/services/prisma/prisma.service.ts b/apps/api/src/services/prisma/prisma.service.ts index 4673cbd19..cdbc1cdfd 100644 --- a/apps/api/src/services/prisma/prisma.service.ts +++ b/apps/api/src/services/prisma/prisma.service.ts @@ -6,6 +6,7 @@ import { OnModuleInit } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; +import { PrismaPg } from '@prisma/adapter-pg'; import { Prisma, PrismaClient } from '@prisma/client'; @Injectable() @@ -14,6 +15,10 @@ export class PrismaService implements OnModuleInit, OnModuleDestroy { public constructor(configService: ConfigService) { + const adapter = new PrismaPg({ + connectionString: configService.get('DATABASE_URL') + }); + let customLogLevels: LogLevel[]; try { @@ -28,6 +33,7 @@ export class PrismaService : []; super({ + adapter, log, errorFormat: 'colorless' }); diff --git a/apps/client/src/app/app.component.ts b/apps/client/src/app/app.component.ts index 201c2f994..dbe616dac 100644 --- a/apps/client/src/app/app.component.ts +++ b/apps/client/src/app/app.component.ts @@ -336,7 +336,9 @@ export class GfAppComponent implements OnInit { if (isDarkTheme) { this.document.body.classList.add('theme-dark'); + this.document.body.classList.remove('theme-light'); } else { + this.document.body.classList.add('theme-light'); this.document.body.classList.remove('theme-dark'); } diff --git a/apps/client/src/app/components/header/header.component.html b/apps/client/src/app/components/header/header.component.html index 501119b31..94421498c 100644 --- a/apps/client/src/app/components/header/header.component.html +++ b/apps/client/src/app/components/header/header.component.html @@ -2,7 +2,7 @@ @if (user) {
  • +
  • @if (currentRoute !== 'register' && hasPermissionToCreateUser) {
  • +
    +
    +
    +
    +

    Ghostfolio 3.0

    +
    2026-04-23
    + Ghostfolio 3.0 Teaser +
    +
    +

    + Since the + last major version + of Ghostfolio, we have shipped + over 250 releases. The project now counts 275+ contributors from + around the globe and has surpassed 2’300’000 pulls on Docker Hub. + These milestones reflect steady adoption and our focus on + simplifying investment tracking while prioritizing user privacy. +

    +

    + Today’s release marks the next major version in our Open Source + Software (OSS) journey. +

    +
    +
    +

    Introducing Ghostfolio 3.0

    +

    + Ghostfolio 3.0 is the evolution of our + open source wealth management software, with meaningful improvements for both users and developers. We + have refreshed the user interface, expanded analytics, improved + stability, added more languages, and updated the technology stack to + support these changes. Here is a closer look at a selection of what + you can expect from this + release, alongside + many smaller additions and enhancements. +

    +

    Refreshed User Interface

    +

    + Ghostfolio 3.0 comes with a refreshed user interface that modernizes + the visual appearance of the application. The updated design is + cleaner, with refined components and improved consistency across the + platform. +

    +

    Comprehensive Analytics

    +

    + This release provides a broader set of tools to help you understand + your portfolio. Ghostfolio X-ray uses static analysis to highlight + potential issues and risks, with rules that can now be customized to + match your investment strategy. +

    +

    Extended Multilanguage Support

    +

    + Largely driven by contributions from the community, Ghostfolio now + supports more languages than ever. The application is available in a + growing number of languages, making it accessible to a broader + audience of investors around the world. +

    +

    Reliable Stability

    +

    + A wealth management platform needs to be reliable. With Ghostfolio + 3.0, we have further strengthened the robustness of our + architecture, so you can rely on Ghostfolio across different + market conditions. +

    +

    Empowering Self-Hosting

    +

    + This release simplifies and extends the self-hosting experience. A + major addition is that self-hosters can now fully benefit from + Ghostfolio Premium to + make use of a professional data provider. This gives you full + control over your infrastructure while still having access to + high-quality market data for portfolio analytics. +

    +

    Updated Technology Stack

    +

    + Under the hood, Ghostfolio 3.0 has been upgraded to + Angular 21, + Nest.js 11, + Prisma 7, and + Nx 22. Keeping the + technology stack up to date helps us provide a solid foundation for + users and developers. +

    +
    +
    +

    Thriving Ghostfolio Community

    +

    + Ghostfolio is built in public, and its community plays a central + role in shaping the open source project. Here are some highlights of + the community growth: +

    +
      +
    • + Ghostfolio has accumulated 8’000+ stars on + GitHub, reflecting the growing interest and trust in the project. +
    • +
    • + The + Slack + community has grown to over 1’250 members, where + investors exchange ideas and help each other. +
    • +
    • + Over + 700 investors and personal finance enthusiasts + follow Ghostfolio on + X + (formerly Twitter) for updates and discussions. +
    • +
    +

    + There is much more to come. If you are not part of the community + yet, we would love to have you on board. +

    +

    + Join our Slack community: Connect with fellow + investors, share insights, and stay updated by joining our + Slack + community. +

    +

    + Follow us on X: For release updates and market + insights, follow + Ghostfolio on X + to stay informed. +

    +

    + Give us a Star: If Ghostfolio has been useful to + you, please consider giving us a star on + GitHub. Your support helps us continue improving Ghostfolio. +

    +

    + Become a contributor: Interested in getting + involved? We welcome contributions from developers who are willing + to dive into open source and personal finance topics. + Join our developer community + and help shape the future of Ghostfolio. +

    +
    +
    +

    + Ghostfolio 3.0 is the result of countless contributions, feedback, + and shared passion for open source and personal finance. Whether you + have been with us from the start or are just discovering the + project, thank you for being part of this community. +

    +

    Thomas from Ghostfolio

    +
    +
    +
      +
    • + Angular +
    • +
    • + Announcement +
    • +
    • + Collaboration +
    • +
    • + Community +
    • +
    • + Contribution +
    • +
    • + Evolution +
    • +
    • + Fintech +
    • +
    • + Ghostfolio +
    • +
    • + Ghostfolio 3.0 +
    • +
    • + Ghostfolio Premium +
    • +
    • + Internationalization +
    • +
    • + Investment +
    • +
    • + Nest.js +
    • +
    • + Nx +
    • +
    • + Open Source +
    • +
    • + OSS +
    • +
    • + Personal Finance +
    • +
    • + Platform +
    • +
    • + Portfolio +
    • +
    • + Prisma +
    • +
    • + Privacy +
    • +
    • + Release +
    • +
    • + Self-Hosting +
    • +
    • + Software +
    • +
    • + Stack +
    • +
    • + Technology +
    • +
    • + Wealth Management +
    • +
    +
    + +
    +
    +
    +
  • diff --git a/apps/client/src/app/pages/blog/blog-page.html b/apps/client/src/app/pages/blog/blog-page.html index e84cb303d..d60fd1bcf 100644 --- a/apps/client/src/app/pages/blog/blog-page.html +++ b/apps/client/src/app/pages/blog/blog-page.html @@ -8,6 +8,32 @@ finance + + + + + @if (hasPermissionForSubscription) { diff --git a/apps/client/src/app/pages/blog/blog-page.routes.ts b/apps/client/src/app/pages/blog/blog-page.routes.ts index 90d27bdfb..9afb35d46 100644 --- a/apps/client/src/app/pages/blog/blog-page.routes.ts +++ b/apps/client/src/app/pages/blog/blog-page.routes.ts @@ -218,5 +218,14 @@ export const routes: Routes = [ (c) => c.BlackWeeks2025PageComponent ), title: 'Black Weeks 2025' + }, + { + canActivate: [AuthGuard], + path: '2026/04/ghostfolio-3', + loadComponent: () => + import('./2026/04/ghostfolio-3/ghostfolio-3-page.component').then( + (c) => c.Ghostfolio3PageComponent + ), + title: 'Ghostfolio 3.0' } ]; diff --git a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html index c4e8f610b..4c5c61bd8 100644 --- a/apps/client/src/app/pages/portfolio/analysis/analysis-page.html +++ b/apps/client/src/app/pages/portfolio/analysis/analysis-page.html @@ -75,70 +75,68 @@ } - @if (user?.settings?.isExperimentalFeatures) { -
    -
    - - - Total amount - - -
    -
    - - - Change with currency effect - - -
    -
    - - - Performance with currency effect - - -
    +
    +
    + + + Total amount + +
    - } +
    + + + Change with currency effect + + +
    +
    + + + Performance with currency effect + + +
    +
    diff --git a/apps/client/src/assets/images/blog/ghostfolio-3.jpg b/apps/client/src/assets/images/blog/ghostfolio-3.jpg new file mode 100644 index 000000000..2a2c787e8 Binary files /dev/null and b/apps/client/src/assets/images/blog/ghostfolio-3.jpg differ diff --git a/apps/client/src/styles.scss b/apps/client/src/styles.scss index f5a4e9c80..ecb53b9ab 100644 --- a/apps/client/src/styles.scss +++ b/apps/client/src/styles.scss @@ -1,3 +1,5 @@ +@use '@angular/material' as mat; + @import './styles/bootstrap'; @import './styles/table'; @import './styles/variables'; @@ -256,46 +258,10 @@ body { } } - .mat-mdc-card { - --mat-card-elevated-container-color: var(--dark-background); - --mat-card-outlined-container-color: var(--dark-background); - } - - .mat-mdc-fab { - &.mat-primary { - --mat-fab-icon-color: rgba(var(--dark-primary-text)); - --mat-mdc-fab-color: rgba(var(--dark-primary-text)); - } - } - .mat-mdc-paginator { background-color: rgba(var(--palette-foreground-base-dark), 0.02); } - .mat-mdc-slide-toggle { - .mdc-switch__track { - --mat-slide-toggle-selected-focus-track-color: rgba( - 255, - 255, - 255, - 0.12 - ); - --mat-slide-toggle-selected-hover-track-color: rgba( - 255, - 255, - 255, - 0.12 - ); - --mat-slide-toggle-selected-pressed-track-color: rgba( - 255, - 255, - 255, - 0.12 - ); - --mat-slide-toggle-selected-track-color: rgba(255, 255, 255, 0.12); - } - } - .mdc-button { &.mat-accent, &.mat-primary { @@ -305,10 +271,6 @@ body { .page { &.has-tabs { - .mat-mdc-tab-nav-bar { - --mat-tab-inactive-label-text-color: rgba(var(--light-primary-text)); - } - @media (min-width: 576px) { .mat-mdc-tab-header { background-color: rgba(var(--palette-foreground-base-dark), 0.02); @@ -443,8 +405,6 @@ ngx-skeleton-loader { .mat-mdc-card { .mat-mdc-card-title { - --mat-card-title-text-line-height: 1.2; - margin-bottom: 0.5rem; } } @@ -468,15 +428,6 @@ ngx-skeleton-loader { } } -.mat-mdc-fab { - color: var(--mat-mdc-fab-color, inherit) !important; - - &.mat-primary { - --mat-fab-icon-color: rgba(var(--light-primary-text)); - --mat-mdc-fab-color: rgba(var(--light-primary-text)); - } -} - .mat-mdc-form-field { &.without-hint { .mat-mdc-form-field-subscript-wrapper { @@ -517,15 +468,6 @@ ngx-skeleton-loader { } } -.mat-mdc-slide-toggle { - .mdc-switch__track { - --mat-slide-toggle-selected-focus-track-color: rgba(0, 0, 0, 0.12); - --mat-slide-toggle-selected-hover-track-color: rgba(0, 0, 0, 0.12); - --mat-slide-toggle-selected-pressed-track-color: rgba(0, 0, 0, 0.12); - --mat-slide-toggle-selected-track-color: rgba(0, 0, 0, 0.12); - } -} - .mat-stepper-vertical, .mat-stepper-horizontal { background: transparent !important; @@ -587,32 +529,23 @@ ngx-skeleton-loader { } } - .mat-mdc-tab-nav-bar { - --mat-tab-active-focus-indicator-color: transparent; - --mat-tab-active-hover-indicator-color: transparent; - --mat-tab-inactive-label-text-color: rgba(var(--dark-primary-text)); - --mat-tab-active-indicator-color: transparent; - } - .mat-mdc-tab-nav-panel { padding: 2rem 0; } - @media (max-width: 575.98px) { - .mat-mdc-tab-link { - --mat-tab-container-height: 3rem; - } - } - @media (min-width: 576px) { flex-direction: row-reverse; + @include mat.tabs-overrides( + ( + divider-height: 0 + ) + ); + .mat-mdc-tab-header { background-color: rgba(var(--palette-foreground-base), 0.02); padding: 2rem 0; width: 14rem; - --mat-tab-label-text-tracking: normal; - --mat-tab-container-height: 2rem; .mat-mdc-tab-links { flex-direction: column; diff --git a/apps/client/src/styles/theme.scss b/apps/client/src/styles/theme.scss index 8dd6d8e36..7244b007d 100644 --- a/apps/client/src/styles/theme.scss +++ b/apps/client/src/styles/theme.scss @@ -1,109 +1,437 @@ @use '@angular/material' as mat; +@use 'sass:map'; -@use './variables.scss' as variables; - -$gf-primary: ( - 50: var(--gf-theme-primary-50), - 100: var(--gf-theme-primary-100), - 200: var(--gf-theme-primary-200), - 300: var(--gf-theme-primary-300), - 400: var(--gf-theme-primary-400), - 500: var(--gf-theme-primary-500), - 600: var(--gf-theme-primary-600), - 700: var(--gf-theme-primary-700), - 800: var(--gf-theme-primary-800), - 900: var(--gf-theme-primary-900), - A100: var(--gf-theme-primary-A100), - A200: var(--gf-theme-primary-A200), - A400: var(--gf-theme-primary-A400), - A700: var(--gf-theme-primary-A700), - contrast: ( - 50: variables.$dark-primary-text, - 100: variables.$dark-primary-text, - 200: variables.$dark-primary-text, - 300: variables.$light-primary-text, - 400: variables.$light-primary-text, - 500: variables.$light-primary-text, - 600: variables.$light-primary-text, - 700: variables.$light-primary-text, - 800: variables.$light-primary-text, - 900: variables.$light-primary-text, - A100: variables.$dark-primary-text, - A200: variables.$light-primary-text, - A400: variables.$light-primary-text, - A700: variables.$light-primary-text - ) -); +$dark-primary-text: rgba(black, 0.87); +$light-primary-text: white; -$gf-secondary: ( - 50: var(--gf-theme-secondary-50), - 100: var(--gf-theme-secondary-100), - 200: var(--gf-theme-secondary-200), - 300: var(--gf-theme-secondary-300), - 400: var(--gf-theme-secondary-400), - 500: var(--gf-theme-secondary-500), - 600: var(--gf-theme-secondary-600), - 700: var(--gf-theme-secondary-700), - 800: var(--gf-theme-secondary-800), - 900: var(--gf-theme-secondary-900), - A100: var(--gf-theme-secondary-A100), - A200: var(--gf-theme-secondary-A200), - A400: var(--gf-theme-secondary-A400), - A700: var(--gf-theme-secondary-A700), - contrast: ( - 50: variables.$dark-primary-text, - 100: variables.$dark-primary-text, - 200: variables.$dark-primary-text, - 300: variables.$light-primary-text, - 400: variables.$light-primary-text, - 500: variables.$light-primary-text, - 600: variables.$light-primary-text, - 700: variables.$light-primary-text, - 800: variables.$light-primary-text, - 900: variables.$light-primary-text, - A100: variables.$dark-primary-text, - A200: variables.$light-primary-text, - A400: variables.$light-primary-text, - A700: variables.$light-primary-text +$_palettes: ( + primary: ( + 0: #000000, + 10: #00201f, + 20: #003736, + 25: #004342, + 30: #00504e, + 35: #005d5b, + 40: #006a68, + 50: #008583, + 60: #00a19f, + 70: #11bebc, + 80: #47dbd7, + 90: #6bf7f4, + 95: #affffc, + 98: #e3fffd, + 99: #f2fffe, + 100: #ffffff + ), + secondary: ( + 0: #000000, + 10: #001d36, + 20: #003258, + 25: #003d6a, + 30: #00497c, + 35: #00558f, + 40: #0061a3, + 50: #267bc3, + 60: #4895df, + 70: #66b0fb, + 80: #9ecaff, + 90: #d1e4ff, + 95: #e9f1ff, + 98: #f8f9ff, + 99: #fdfcff, + 100: #ffffff + ), + tertiary: ( + 0: #000000, + 10: #031d35, + 20: #1b324b, + 25: #273d57, + 30: #324863, + 35: #3e546f, + 40: #4a607b, + 50: #637995, + 60: #7c92b0, + 70: #97adcc, + 80: #b2c8e8, + 90: #d2e4ff, + 95: #eaf1ff, + 98: #f8f9ff, + 99: #fdfcff, + 100: #ffffff + ), + neutral: ( + 0: #000000, + 10: #191c1c, + 20: #2d3131, + 25: #383c3c, + 30: #444747, + 35: #4f5353, + 40: #5b5f5e, + 50: #747877, + 60: #8e9191, + 70: #a9acab, + 80: #c4c7c6, + 90: #e0e3e2, + 95: #eff1f0, + 98: #f7faf9, + 99: #fafdfc, + 100: #ffffff, + 4: #0b0f0f, + 6: #101414, + 12: #1d2020, + 17: #272b2a, + 22: #323535, + 24: #363a39, + 87: #d8dada, + 92: #e6e9e8, + 94: #eceeed, + 96: #f2f4f3 + ), + neutral-variant: ( + 0: #000000, + 10: #141d1d, + 20: #293232, + 25: #343d3d, + 30: #3f4948, + 35: #4a5454, + 40: #566060, + 50: #6f7978, + 60: #889392, + 70: #a3adac, + 80: #bec9c7, + 90: #dae5e3, + 95: #e8f3f2, + 98: #f1fbfa, + 99: #f4fefd, + 100: #ffffff + ), + error: ( + 0: #000000, + 10: #410002, + 20: #690005, + 25: #7e0007, + 30: #93000a, + 35: #a80710, + 40: #ba1a1a, + 50: #de3730, + 60: #ff5449, + 70: #ff897d, + 80: #ffb4ab, + 90: #ffdad6, + 95: #ffedea, + 98: #fff8f7, + 99: #fffbff, + 100: #ffffff ) ); -$gf-typography: mat.m2-define-typography-config(); - -// Create default theme -$gf-theme-default: mat.m2-define-light-theme( - ( - color: ( - accent: mat.m2-define-palette($gf-secondary, 500, 900, A100), - primary: mat.m2-define-palette($gf-primary) - ), - density: -3, - typography: $gf-typography - ) +$_rest: ( + secondary: map.get($_palettes, secondary), + neutral: map.get($_palettes, neutral), + neutral-variant: map.get($_palettes, neutral-variant), + error: map.get($_palettes, error) ); +$_primary: map.merge(map.get($_palettes, primary), $_rest); +$_tertiary: map.merge(map.get($_palettes, tertiary), $_rest); -@include mat.all-component-themes($gf-theme-default); - -// Create dark theme -$gf-theme-dark: mat.m2-define-dark-theme( - ( - color: ( - accent: mat.m2-define-palette($gf-secondary, 500, 900, A100), - primary: mat.m2-define-palette($gf-primary) - ), - density: -3, - typography: $gf-typography - ) -); +@include mat.app-background(); +@include mat.button-density(0); +@include mat.elevation-classes(); +@include mat.table-density(-1); + +// $gf-typography: mat.m2-define-typography-config(); + +.theme-light { + $gf-theme-default: mat.define-theme( + ( + color: ( + primary: $_primary, + theme-type: light, + tertiary: $_tertiary + ), + density: ( + scale: -3 + ), + // typography: $gf-typography + ) + ); + + @include mat.all-component-themes($gf-theme-default); + + @include mat.autocomplete-overrides( + ( + background-color: var(--light-background) + ) + ); + + @include mat.button-overrides( + ( + outlined-label-text-color: var(--dark-primary-text), + text-label-text-color: var(--dark-primary-text) + ) + ); + + @include mat.button-toggle-overrides( + ( + selected-state-background-color: rgba(var(--dark-dividers)), + selected-state-text-color: var(--dark-primary-text) + ) + ); + + @include mat.card-overrides( + ( + outlined-container-color: var(--light-background), + outlined-outline-color: rgba(var(--dark-dividers)), + title-text-line-height: 1.2 + ) + ); + + @include mat.datepicker-overrides( + ( + calendar-container-background-color: var(--light-background) + ) + ); + + @include mat.dialog-overrides( + ( + container-color: var(--light-background) + ) + ); + + @include mat.menu-overrides( + ( + container-color: var(--light-background) + ) + ); + + @include mat.select-overrides( + ( + panel-background-color: var(--light-background) + ) + ); + + @include mat.slide-toggle-overrides( + ( + selected-track-outline-color: rgba(var(--dark-dividers)), + track-outline-color: rgba(var(--dark-dividers)) + ) + ); + + @include mat.table-overrides( + ( + row-item-outline-color: rgba(var(--dark-dividers)) + ) + ); +} .theme-dark { - @include mat.all-component-colors($gf-theme-dark); + $gf-theme-dark: mat.define-theme( + ( + color: ( + primary: $_primary, + theme-type: dark, + tertiary: $_tertiary + ), + density: ( + scale: -3 + ), + // typography: $gf-typography + ) + ); + + @include mat.all-component-themes($gf-theme-dark); + + @include mat.button-overrides( + ( + outlined-label-text-color: var(--light-primary-text), + text-label-text-color: var(--light-primary-text) + ) + ); + + @include mat.button-toggle-overrides( + ( + selected-state-background-color: rgba(var(--light-dividers)), + selected-state-text-color: var(--light-primary-text) + ) + ); + + @include mat.card-overrides( + ( + outlined-container-color: var(--dark-background), + outlined-outline-color: rgba(var(--light-dividers)), + title-text-line-height: 1.2 + ) + ); + + @include mat.slide-toggle-overrides( + ( + selected-track-outline-color: rgba(var(--light-dividers)), + track-outline-color: rgba(var(--light-dividers)) + ) + ); + + @include mat.table-overrides( + ( + row-item-outline-color: rgba(var(--light-dividers)) + ) + ); } -@include mat.button-density(0); -@include mat.elevation-classes(); -@include mat.app-background(); -@include mat.table-density(-1); +.theme-dark, +.theme-light { + @media (max-width: 575.98px) { + @include mat.dialog-overrides( + ( + container-shape: 4px + ) + ); + + .page.has-tabs { + @include mat.tabs-overrides( + ( + container-height: 3rem + ) + ); + } + } + + @media (min-width: 576px) { + .page.has-tabs { + @include mat.tabs-overrides( + ( + container-height: 2rem + ) + ); + } + } + + @include mat.badge-overrides( + ( + background-color: var(--gf-theme-primary-500) + ) + ); + + @include mat.button-overrides( + ( + filled-container-color: var(--gf-theme-primary-500), + filled-horizontal-padding: 1rem, + outlined-horizontal-padding: 1rem, + text-horizontal-padding: 1rem + ) + ); + + @include mat.checkbox-overrides( + ( + selected-icon-color: var(--gf-theme-primary-500) + ) + ); + + @include mat.datepicker-overrides( + ( + calendar-container-elevation-shadow: var( + --mat-select-container-elevation-shadow + ), + calendar-date-selected-state-background-color: var(--gf-theme-primary-500), + calendar-date-today-selected-state-outline-color: var( + --gf-theme-primary-500 + ) + ) + ); + + @include mat.dialog-overrides( + ( + container-max-width: 80vw, + container-shape: 8px, + container-small-max-width: 96vw + ) + ); + + @include mat.fab-overrides( + ( + container-color: var(--gf-theme-primary-500) + ) + ); + + @include mat.form-field-overrides( + ( + outlined-focus-label-text-color: var(--gf-theme-primary-500), + outlined-focus-outline-color: var(--gf-theme-primary-500) + ) + ); + + @include mat.progress-bar-overrides( + ( + active-indicator-color: var(--gf-theme-primary-500) + ) + ); + + @include mat.slide-toggle-overrides( + ( + selected-focus-handle-color: var(--gf-theme-primary-500), + selected-focus-track-color: transparent, + selected-handle-color: var(--gf-theme-primary-500), + selected-hover-handle-color: var(--gf-theme-primary-500), + selected-hover-track-color: transparent, + selected-pressed-handle-color: var(--gf-theme-primary-500), + selected-pressed-track-color: transparent, + selected-track-color: transparent, + unselected-hover-track-color: transparent, + unselected-track-color: transparent + ) + ); + + @include mat.slider-overrides( + ( + active-track-color: var(--gf-theme-primary-500), + focus-handle-color: var(--gf-theme-primary-500), + handle-color: var(--gf-theme-primary-500) + ) + ); + + @include mat.stepper-overrides( + ( + header-selected-state-icon-background-color: var(--gf-theme-primary-500) + ) + ); + + @include mat.tabs-overrides( + ( + active-focus-label-text-color: var(--gf-theme-primary-500), + active-hover-label-text-color: var(--gf-theme-primary-500), + active-label-text-color: var(--gf-theme-primary-500), + active-ripple-color: var(--gf-theme-primary-500), + inactive-ripple-color: var(--gf-theme-primary-500) + ) + ); + + .mat-accent { + @include mat.button-overrides( + ( + filled-container-color: var(--gf-theme-secondary-500), + outlined-label-text-color: var(--gf-theme-secondary-500) + ) + ); + } + + .mat-warn { + @include mat.button-overrides( + ( + filled-container-color: #f44336, + filled-label-text-color: white, + outlined-label-text-color: #f44336 + ) + ); + } + + .page.has-tabs { + @include mat.tabs-overrides( + ( + active-indicator-height: 0, + label-text-tracking: normal + ) + ); + } +} :root { --gf-theme-alpha-hover: 0.04; diff --git a/libs/common/jest.config.ts b/libs/common/jest.config.ts index 5002d4d3b..f6cd07ae4 100644 --- a/libs/common/jest.config.ts +++ b/libs/common/jest.config.ts @@ -2,6 +2,7 @@ export default { displayName: 'common', + setupFilesAfterEnv: ['/src/test-setup.ts'], globals: {}, transform: { '^.+\\.[tj]sx?$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }] diff --git a/libs/common/src/test-setup.ts b/libs/common/src/test-setup.ts new file mode 100644 index 000000000..75cf71ea5 --- /dev/null +++ b/libs/common/src/test-setup.ts @@ -0,0 +1,3 @@ +import { TextDecoder, TextEncoder } from 'node:util'; + +Object.assign(global, { TextDecoder, TextEncoder }); diff --git a/package-lock.json b/package-lock.json index 6117044e0..7b05e00ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ghostfolio", - "version": "2.255.0", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ghostfolio", - "version": "2.255.0", + "version": "3.0.0", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { @@ -41,7 +41,8 @@ "@nestjs/schedule": "6.1.3", "@nestjs/serve-static": "5.0.5", "@openrouter/ai-sdk-provider": "0.7.2", - "@prisma/client": "6.19.3", + "@prisma/adapter-pg": "7.7.0", + "@prisma/client": "7.7.0", "@simplewebauthn/browser": "13.2.2", "@simplewebauthn/server": "13.2.2", "ai": "4.3.16", @@ -150,7 +151,7 @@ "nx": "22.6.5", "prettier": "3.8.2", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.19.3", + "prisma": "7.7.0", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.4.0", @@ -3891,6 +3892,36 @@ "node": ">=14.17.0" } }, + "node_modules/@electric-sql/pglite": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.4.1.tgz", + "integrity": "sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@electric-sql/pglite-socket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.1.1.tgz", + "integrity": "sha512-p2hoXw3Z3LQHwTeikdZNsFBOvXGqKY2hk51BBw+8NKND8eoH+8LFOtW9Z8CQKmTJ2qqGYu82ipqiyFZOTTXNfw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "pglite-server": "dist/scripts/server.js" + }, + "peerDependencies": { + "@electric-sql/pglite": "0.4.1" + } + }, + "node_modules/@electric-sql/pglite-tools": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.3.1.tgz", + "integrity": "sha512-C+T3oivmy9bpQvSxVqXA1UDY8cB9Eb9vZHL9zxWwEUfDixbXv4G3r2LjoTdR33LD8aomR3O9ZXEO3XEwr/cUCA==", + "devOptional": true, + "license": "Apache-2.0", + "peerDependencies": { + "@electric-sql/pglite": "0.4.1" + } + }, "node_modules/@emnapi/core": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", @@ -4533,10 +4564,10 @@ "license": "MIT" }, "node_modules/@hono/node-server": { - "version": "1.19.13", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", - "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==", - "dev": true, + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", + "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=18.14.1" @@ -11339,18 +11370,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@prisma/adapter-pg": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.7.0.tgz", + "integrity": "sha512-q33Ta8sKbgzEpAy0lx45tAq//yMv0qcb+8nj+TCA3P4wiAY+OBFEFk/NDkZncAfHaNJeGo5WJpJdpbL+ijYx8g==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/driver-adapter-utils": "7.7.0", + "@types/pg": "^8.16.0", + "pg": "^8.16.3", + "postgres-array": "3.0.4" + } + }, "node_modules/@prisma/client": { - "version": "6.19.3", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz", - "integrity": "sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==", - "hasInstallScript": true, + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.7.0.tgz", + "integrity": "sha512-5Ar4OsZpJ54s21sy5oDNNW9gQtd4NuxCaiM7+JDTOU07D6VvlpLjYzAVCMB1+JzokN+08dAVomlx+b7bhJd3ww==", "license": "Apache-2.0", + "dependencies": { + "@prisma/client-runtime-utils": "7.7.0" + }, "engines": { - "node": ">=18.18" + "node": "^20.19 || ^22.12 || >=24.0" }, "peerDependencies": { "prisma": "*", - "typescript": ">=5.1.0" + "typescript": ">=5.4.0" }, "peerDependenciesMeta": { "prisma": { @@ -11361,67 +11406,338 @@ } } }, + "node_modules/@prisma/client-runtime-utils": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.7.0.tgz", + "integrity": "sha512-BLyd0UpFYOtyJFTHm7jS9vesHW7P83abibodQMiIofqjBKzDHQ1VAsQkdfvXyYDkPlONPfOTz7/rv3x/+CQqvQ==", + "license": "Apache-2.0" + }, "node_modules/@prisma/config": { - "version": "6.19.3", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.3.tgz", - "integrity": "sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.7.0.tgz", + "integrity": "sha512-hmPI3tKLO2aP0Y5vugbjcnA9qqlfJndiT6ds4tw28U5hNHLWg+mHJEWAhjsSPgxjtmxhJ/EDIeIlyh+3Us0OPg==", "devOptional": true, "license": "Apache-2.0", "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", - "effect": "3.21.0", + "effect": "3.20.0", "empathic": "2.0.0" } }, "node_modules/@prisma/debug": { - "version": "6.19.3", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.3.tgz", - "integrity": "sha512-ljkJ+SgpXNktLG0Q/n4JGYCkKf0f8oYLyjImS2I8e2q2WCfdRRtWER062ZV/ixaNP2M2VKlWXVJiGzZaUgbKZw==", - "devOptional": true, + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.7.0.tgz", + "integrity": "sha512-12J62XdqCmpiwJHhHdQxZeY3ckVCWIFmcJP8hg5dPTceeiQ0wiojXGFYTluKqFQfu46fRLgb/rLALZMAx3+dTA==", "license": "Apache-2.0" }, + "node_modules/@prisma/dev": { + "version": "0.24.3", + "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.24.3.tgz", + "integrity": "sha512-ffHlQuKXZiaDt9Go0OnCTdJZrHxK0k7omJKNV86/VjpsXu5EIHZLK0T7JSWgvNlJwh56kW9JFu9v0qJciFzepg==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "@electric-sql/pglite": "0.4.1", + "@electric-sql/pglite-socket": "0.1.1", + "@electric-sql/pglite-tools": "0.3.1", + "@hono/node-server": "1.19.11", + "@prisma/get-platform": "7.2.0", + "@prisma/query-plan-executor": "7.2.0", + "@prisma/streams-local": "0.1.2", + "foreground-child": "3.3.1", + "get-port-please": "3.2.0", + "hono": "^4.12.8", + "http-status-codes": "2.3.0", + "pathe": "2.0.3", + "proper-lockfile": "4.1.2", + "remeda": "2.33.4", + "std-env": "3.10.0", + "valibot": "1.2.0", + "zeptomatch": "2.1.0" + } + }, + "node_modules/@prisma/driver-adapter-utils": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.7.0.tgz", + "integrity": "sha512-gZXREeu6mOk7zXfGFJgh86p7Vhj0sXNKp+4Cg1tWYo7V2dfncP2qxS2BiTmbIIha8xPqItkl0WSw38RuSq1HoQ==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.7.0" + } + }, "node_modules/@prisma/engines": { - "version": "6.19.3", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.3.tgz", - "integrity": "sha512-RSYxtlYFl5pJ8ZePgMv0lZ9IzVCOdTPOegrs2qcbAEFrBI1G33h6wyC9kjQvo0DnYEhEVY0X4LsuFHXLKQk88g==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.7.0.tgz", + "integrity": "sha512-7fmcbT7HHXBq/b+3h/dO1JI3fd8l8q7erf7xP7pRprh58hmSSnG8mg9K3yjW3h9WaHWUwngVFpSxxxivaitQ2w==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.19.3", - "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "@prisma/fetch-engine": "6.19.3", - "@prisma/get-platform": "6.19.3" + "@prisma/debug": "7.7.0", + "@prisma/engines-version": "7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711", + "@prisma/fetch-engine": "7.7.0", + "@prisma/get-platform": "7.7.0" } }, "node_modules/@prisma/engines-version": { - "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", - "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "version": "7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711.tgz", + "integrity": "sha512-r51DLcJ8bDRSrBEJF3J4cinoWyGA7rfP2mG6lD90VqIbGNOkbfcLcXalSVjq5Y6brQS3vcjrq4GbyUb1Cb7vkw==", "devOptional": true, "license": "Apache-2.0" }, + "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.7.0.tgz", + "integrity": "sha512-MEUNzvKxvYnJ7kgvd6oNRnMmmiGNS9TYLB2weMeIXplnHdL/UWEGnvavYGnN7KLJ2n0iI4dDAyzSkHI3c7AscQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.7.0" + } + }, "node_modules/@prisma/fetch-engine": { - "version": "6.19.3", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.3.tgz", - "integrity": "sha512-tKtl/qco9Nt7LU5iKhpultD8O4vMCZcU2CHjNTnRrL1QvSUr5W/GcyFPjNL87GtRrwBc7ubXXD9xy4EvLvt8JA==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.7.0.tgz", + "integrity": "sha512-TfyzveBQoK4xALzsTpVhB/0KG1N8zOK0ap+RnBMkzGUu3f98fnQ4QtXa2wlKPhsO2X8a3N5ugFQgcKNoHGmDfw==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.19.3", - "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "@prisma/get-platform": "6.19.3" + "@prisma/debug": "7.7.0", + "@prisma/engines-version": "7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711", + "@prisma/get-platform": "7.7.0" + } + }, + "node_modules/@prisma/fetch-engine/node_modules/@prisma/get-platform": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.7.0.tgz", + "integrity": "sha512-MEUNzvKxvYnJ7kgvd6oNRnMmmiGNS9TYLB2weMeIXplnHdL/UWEGnvavYGnN7KLJ2n0iI4dDAyzSkHI3c7AscQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.7.0" } }, "node_modules/@prisma/get-platform": { - "version": "6.19.3", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.3.tgz", - "integrity": "sha512-xFj1VcJ1N3MKooOQAGO0W5tsd0W2QzIvW7DD7c/8H14Zmp4jseeWAITm+w2LLoLrlhoHdPPh0NMZ8mfL6puoHA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.2.0.tgz", + "integrity": "sha512-k1V0l0Td1732EHpAfi2eySTezyllok9dXb6UQanajkJQzPUGi3vO2z7jdkz67SypFTdmbnyGYxvEvYZdZsMAVA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "7.2.0" + } + }, + "node_modules/@prisma/get-platform/node_modules/@prisma/debug": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.2.0.tgz", + "integrity": "sha512-YSGTiSlBAVJPzX4ONZmMotL+ozJwQjRmZweQNIq/ER0tQJKJynNkRB3kyvt37eOfsbMCXk3gnLF6J9OJ4QWftw==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/query-plan-executor": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@prisma/query-plan-executor/-/query-plan-executor-7.2.0.tgz", + "integrity": "sha512-EOZmNzcV8uJ0mae3DhTsiHgoNCuu1J9mULQpGCh62zN3PxPTd+qI9tJvk5jOst8WHKQNwJWR3b39t0XvfBB0WQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/streams-local": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@prisma/streams-local/-/streams-local-0.1.2.tgz", + "integrity": "sha512-l49yTxKKF2odFxaAXTmwmkBKL3+bVQ1tFOooGifu4xkdb9NMNLxHj27XAhTylWZod8I+ISGM5erU1xcl/oBCtg==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "6.19.3" + "ajv": "^8.12.0", + "better-result": "^2.7.0", + "env-paths": "^3.0.0", + "proper-lockfile": "^4.1.2" + }, + "engines": { + "bun": ">=1.3.6", + "node": ">=22.0.0" + } + }, + "node_modules/@prisma/streams-local/node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@prisma/studio-core": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.27.3.tgz", + "integrity": "sha512-AADjNFPdsrglxHQVTmHFqv6DuKQZ5WY4p5/gVFY017twvNrSwpLJ9lqUbYYxEu2W7nbvVxTZA8deJ8LseNALsw==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@radix-ui/react-toggle": "1.1.10", + "chart.js": "4.5.1" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24.0", + "pnpm": "8" + }, + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", + "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@redis/client": { @@ -14531,6 +14847,17 @@ "@types/passport": "*" } }, + "node_modules/@types/pg": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz", + "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, "node_modules/@types/qs": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", @@ -14546,10 +14873,10 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", - "dev": true, + "version": "19.1.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", + "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", + "devOptional": true, "license": "MIT", "peer": true, "dependencies": { @@ -16640,6 +16967,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/axios": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", @@ -16965,6 +17302,13 @@ "node": ">=18.0.0" } }, + "node_modules/better-result": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/better-result/-/better-result-2.8.2.tgz", + "integrity": "sha512-YOf0VSj5nUPI27doTtXF+BBnsiRq3qY7avHqfIWnppxTLGyvkLq1QV2RTxkwoZwJ60ywLfZ0raFF4J/G886i7A==", + "devOptional": true, + "license": "MIT" + }, "node_modules/bidi-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", @@ -18726,7 +19070,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -19631,7 +19975,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peer": true }, @@ -20880,9 +21224,9 @@ "license": "MIT" }, "node_modules/effect": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.21.0.tgz", - "integrity": "sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.20.0.tgz", + "integrity": "sha512-qMLfDJscrNG8p/aw+IkT9W7fgj50Z4wG5bLBy0Txsxz8iUHjDIkOgO3SV0WZfnQbNG2VJYb0b+rDLMrhM4+Krw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -22677,7 +23021,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -23157,6 +23501,16 @@ "node": ">=10" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -23252,6 +23606,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "devOptional": true, + "license": "MIT" + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -23587,6 +23948,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/grammex": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", + "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -23594,6 +23962,13 @@ "dev": true, "license": "MIT" }, + "node_modules/graphmatch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/graphmatch/-/graphmatch-1.1.1.tgz", + "integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/gtoken": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-5.3.2.tgz", @@ -23784,10 +24159,10 @@ } }, "node_modules/hono": { - "version": "4.12.12", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz", - "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==", - "dev": true, + "version": "4.12.14", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz", + "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", + "devOptional": true, "license": "MIT", "engines": { "node": ">=16.9.0" @@ -24957,6 +25332,13 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "devOptional": true, + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -25192,7 +25574,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/isobject": { @@ -29527,6 +29909,13 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "devOptional": true, + "license": "Apache-2.0" + }, "node_modules/long-timeout": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", @@ -29578,6 +29967,22 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "license": "ISC" }, + "node_modules/lru.min": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.3.tgz", + "integrity": "sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, "node_modules/luxon": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", @@ -30238,6 +30643,57 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/mysql2": { + "version": "3.15.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.3.tgz", + "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", + "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -32090,7 +32546,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -32176,6 +32632,104 @@ "devOptional": true, "license": "MIT" }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pg-types/node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -32663,6 +33217,59 @@ "dev": true, "license": "MIT" }, + "node_modules/postgres": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz", + "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==", + "devOptional": true, + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, + "node_modules/postgres-array": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.4.tgz", + "integrity": "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/powershell-utils": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", @@ -32755,26 +33362,34 @@ } }, "node_modules/prisma": { - "version": "6.19.3", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.3.tgz", - "integrity": "sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.7.0.tgz", + "integrity": "sha512-HlgwRBt1uEFB9LStHL4HLYDvoi4BNu1rYA0hPG0zCAEyK9SaZBqp7E5Rjpc3Qh8Lex/ye/svoHZ0OWoFNhWxuQ==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/config": "6.19.3", - "@prisma/engines": "6.19.3" + "@prisma/config": "7.7.0", + "@prisma/dev": "0.24.3", + "@prisma/engines": "7.7.0", + "@prisma/studio-core": "0.27.3", + "mysql2": "3.15.3", + "postgres": "3.4.7" }, "bin": { "prisma": "build/index.js" }, "engines": { - "node": ">=18.18" + "node": "^20.19 || ^22.12 || >=24.0" }, "peerDependencies": { - "typescript": ">=5.1.0" + "better-sqlite3": ">=9.0.0", + "typescript": ">=5.4.0" }, "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, "typescript": { "optional": true } @@ -32832,6 +33447,35 @@ "node": ">= 4" } }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "devOptional": true, + "license": "ISC" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -33031,7 +33675,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", @@ -33304,6 +33948,16 @@ "node": ">= 0.10" } }, + "node_modules/remeda": { + "version": "2.33.4", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz", + "integrity": "sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==", + "devOptional": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/remeda" + } + }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", @@ -34661,7 +35315,7 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" @@ -34791,6 +35445,12 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==", + "devOptional": true + }, "node_modules/serialize-javascript": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", @@ -35016,7 +35676,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -35029,7 +35689,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -35582,6 +36242,15 @@ "wbuf": "^1.7.3" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -35589,6 +36258,16 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/ssri": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", @@ -35656,6 +36335,13 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/stdin-discarder": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.1.tgz", @@ -36632,9 +37318,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "devOptional": true, "license": "MIT", "engines": { @@ -38062,6 +38748,21 @@ "dev": true, "license": "MIT" }, + "node_modules/valibot": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", + "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">=5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/validate-npm-package-name": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", @@ -39234,7 +39935,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -39540,6 +40241,15 @@ "sax": "^1.2.4" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -39796,6 +40506,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zeptomatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz", + "integrity": "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "grammex": "^3.1.11", + "graphmatch": "^1.1.0" + } + }, "node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", diff --git a/package.json b/package.json index 9203f2e30..bd0824c30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ghostfolio", - "version": "2.255.0", + "version": "3.0.0", "homepage": "https://ghostfol.io", "license": "AGPL-3.0", "repository": "https://github.com/ghostfolio/ghostfolio", @@ -86,7 +86,8 @@ "@nestjs/schedule": "6.1.3", "@nestjs/serve-static": "5.0.5", "@openrouter/ai-sdk-provider": "0.7.2", - "@prisma/client": "6.19.3", + "@prisma/adapter-pg": "7.7.0", + "@prisma/client": "7.7.0", "@simplewebauthn/browser": "13.2.2", "@simplewebauthn/server": "13.2.2", "ai": "4.3.16", @@ -195,7 +196,7 @@ "nx": "22.6.5", "prettier": "3.8.2", "prettier-plugin-organize-attributes": "1.0.0", - "prisma": "6.19.3", + "prisma": "7.7.0", "react": "18.2.0", "react-dom": "18.2.0", "replace-in-file": "8.4.0", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 069ed6279..a739ce8ff 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -6,7 +6,6 @@ generator client { datasource db { provider = "postgresql" - url = env("DATABASE_URL") } model Access { diff --git a/prisma/seed.mts b/prisma/seed.mts index 18389aab1..4819e241e 100644 --- a/prisma/seed.mts +++ b/prisma/seed.mts @@ -1,6 +1,11 @@ +import { PrismaPg } from '@prisma/adapter-pg'; import { PrismaClient } from '@prisma/client'; -const prisma = new PrismaClient(); +const adapter = new PrismaPg({ + connectionString: process.env.DATABASE_URL +}); + +const prisma = new PrismaClient({ adapter }); async function main() { await prisma.tag.createMany({