refactor to adapters

This commit is contained in:
2025-12-15 18:34:20 +01:00
parent fc671482c8
commit c817d76092
145 changed files with 906 additions and 361 deletions

View File

@@ -0,0 +1,50 @@
import { Module } from '@nestjs/common';
import { getDataSourceToken } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
const ILogger_TOKEN = 'ILogger_TOKEN';
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
import { ILogger } from '@gridpilot/shared/logging/ILogger';
import { IPageViewRepository } from '@gridpilot/analytics/application/repositories/IPageViewRepository';
import { IEngagementRepository } from '@gridpilot/analytics/domain/repositories/IEngagementRepository';
import { RecordPageViewUseCase } from '@gridpilot/analytics/application/use-cases/RecordPageViewUseCase';
import { RecordEngagementUseCase } from '@gridpilot/analytics/application/use-cases/RecordEngagementUseCase';
import { InMemoryPageViewRepository } from '../../../../adapters/persistence/inmemory/analytics/InMemoryPageViewRepository';
import { TypeOrmEngagementRepository } from '../../../../adapters/persistence/typeorm/analytics/TypeOrmEngagementRepository';
import { ConsoleLogger } from '../../../../adapters/logging/ConsoleLogger';
import { AnalyticsController } from '../../presentation/analytics.controller';
@Module({
imports: [],
controllers: [AnalyticsController],
providers: [
{
provide: ILogger_TOKEN,
useClass: ConsoleLogger,
},
{
provide: IPAGE_VIEW_REPO_TOKEN,
useClass: InMemoryPageViewRepository,
},
{
provide: IENGAGEMENT_REPO_TOKEN,
useFactory: (dataSource: DataSource) => new TypeOrmEngagementRepository(dataSource.manager),
inject: [getDataSourceToken()],
},
{
provide: RecordPageViewUseCase,
useFactory: (repo: IPageViewRepository, logger: ILogger) => new RecordPageViewUseCase(repo, logger),
inject: [IPAGE_VIEW_REPO_TOKEN, ILogger_TOKEN],
},
{
provide: RecordEngagementUseCase,
useFactory: (repo: IEngagementRepository, logger: ILogger) => new RecordEngagementUseCase(repo, logger),
inject: [IENGAGEMENT_REPO_TOKEN, ILogger_TOKEN],
},
],
})
export class AnalyticsModule {}

View File

@@ -1,6 +1,7 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PageViewEntity } from '../analytics/typeorm-page-view.entity';
import { AnalyticsSnapshotOrmEntity } from '../../../../../adapters/persistence/typeorm/analytics/AnalyticsSnapshotOrmEntity';
import { EngagementOrmEntity } from '../../../../../adapters/persistence/typeorm/analytics/EngagementOrmEntity';
@Module({
imports: [
@@ -11,9 +12,10 @@ import { PageViewEntity } from '../analytics/typeorm-page-view.entity';
username: process.env.DATABASE_USER || 'user',
password: process.env.DATABASE_PASSWORD || 'password',
database: process.env.DATABASE_NAME || 'gridpilot',
entities: [PageViewEntity],
entities: [AnalyticsSnapshotOrmEntity, EngagementOrmEntity],
synchronize: true, // Use carefully in production
}),
],
exports: [TypeOrmModule],
})
export class DatabaseModule {}

View File

@@ -1,20 +1,25 @@
import { Controller, Post, Body, Res, HttpStatus } from '@nestjs/common';
import { AnalyticsService } from '../application/analytics/analytics.service';
import { RecordPageViewInput } from '../application/analytics/record-page-view.use-case';
import { RecordEngagementInput, RecordEngagementOutput } from '../application/analytics/record-engagement.use-case';
import type { RecordPageViewInput, RecordPageViewOutput } from '@gridpilot/analytics/application/use-cases/RecordPageViewUseCase';
import type { RecordEngagementInput, RecordEngagementOutput } from '@gridpilot/analytics/application/use-cases/RecordEngagementUseCase';
import { RecordPageViewUseCase } from '@gridpilot/analytics/application/use-cases/RecordPageViewUseCase';
import { RecordEngagementUseCase } from '@gridpilot/analytics/application/use-cases/RecordEngagementUseCase';
import { Response } from 'express';
@Controller('analytics')
export class AnalyticsController {
constructor(private readonly analyticsService: AnalyticsService) {}
constructor(
private readonly recordPageViewUseCase: RecordPageViewUseCase,
private readonly recordEngagementUseCase: RecordEngagementUseCase,
) {}
@Post('page-view')
async recordPageView(
@Body() input: RecordPageViewInput,
@Res() res: Response,
): Promise<void> {
const { pageViewId } = await this.analyticsService.recordPageView(input);
res.status(HttpStatus.CREATED).json({ pageViewId });
const output: RecordPageViewOutput = await this.recordPageViewUseCase.execute(input);
res.status(HttpStatus.CREATED).json(output);
}
@Post('engagement')
@@ -22,7 +27,7 @@ export class AnalyticsController {
@Body() input: RecordEngagementInput,
@Res() res: Response,
): Promise<void> {
const output: RecordEngagementOutput = await this.analyticsService.recordEngagement(input);
const output: RecordEngagementOutput = await this.recordEngagementUseCase.execute(input);
res.status(HttpStatus.CREATED).json(output);
}
}

View File

@@ -1,112 +0,0 @@
import type { AuthService, AuthSession, SignupParams, LoginParams } from './AuthService';
import type { AuthCallbackCommandDTO } from '@gridpilot/identity/application/dto/AuthCallbackCommandDTO';
import type { StartAuthCommandDTO } from '@gridpilot/identity/application/dto/StartAuthCommandDTO';
import { StartAuthUseCase } from '@gridpilot/identity/application/use-cases/StartAuthUseCase';
import { GetCurrentUserSessionUseCase } from '@gridpilot/identity/application/use-cases/GetCurrentUserSessionUseCase';
import { HandleAuthCallbackUseCase } from '@gridpilot/identity/application/use-cases/HandleAuthCallbackUseCase';
import { LogoutUseCase } from '@gridpilot/identity/application/use-cases/LogoutUseCase';
import { SignupWithEmailUseCase } from '@gridpilot/identity/application/use-cases/SignupWithEmailUseCase';
import { LoginWithEmailUseCase } from '@gridpilot/identity/application/use-cases/LoginWithEmailUseCase';
import { CookieIdentitySessionAdapter } from '@gridpilot/identity/infrastructure/session/CookieIdentitySessionAdapter';
import { IracingDemoIdentityProviderAdapter } from '@gridpilot/identity/infrastructure/providers/IracingDemoIdentityProviderAdapter';
import { InMemoryUserRepository } from '@gridpilot/identity/infrastructure/repositories/InMemoryUserRepository';
import type { IUserRepository } from '@gridpilot/identity/domain/repositories/IUserRepository';
import type { ILogger } from '@gridpilot/shared/logging/ILogger';
// Singleton user repository to persist across requests (in-memory demo)
let userRepositoryInstance: IUserRepository | null = null;
function getUserRepository(logger: ILogger): IUserRepository {
if (!userRepositoryInstance) {
userRepositoryInstance = new InMemoryUserRepository(logger);
}
return userRepositoryInstance;
}
export class InMemoryAuthService implements AuthService {
private readonly logger: ILogger;
constructor(logger: ILogger) {
this.logger = logger;
}
async getCurrentSession(): Promise<AuthSession | null> {
const sessionPort = new CookieIdentitySessionAdapter();
const useCase = new GetCurrentUserSessionUseCase(sessionPort);
return useCase.execute();
}
async signupWithEmail(params: SignupParams): Promise<AuthSession> {
const userRepository = getUserRepository();
const sessionPort = new CookieIdentitySessionAdapter();
const useCase = new SignupWithEmailUseCase(userRepository, sessionPort);
const result = await useCase.execute({
email: params.email,
password: params.password,
displayName: params.displayName,
});
return result.session;
}
async loginWithEmail(params: LoginParams): Promise<AuthSession> {
const userRepository = getUserRepository();
const sessionPort = new CookieIdentitySessionAdapter();
const useCase = new LoginWithEmailUseCase(userRepository, sessionPort);
return useCase.execute({
email: params.email,
password: params.password,
});
}
async startIracingAuthRedirect(
returnTo?: string,
): Promise<{ redirectUrl: string; state: string }> {
const provider = new IracingDemoIdentityProviderAdapter();
const useCase = new StartAuthUseCase(provider);
const command: StartAuthCommandDTO = returnTo
? {
provider: 'IRACING_DEMO',
returnTo,
}
: {
provider: 'IRACING_DEMO',
};
return useCase.execute(command);
}
async loginWithIracingCallback(params: {
code: string;
state: string;
returnTo?: string;
}): Promise<AuthSession> {
const provider = new IracingDemoIdentityProviderAdapter();
const sessionPort = new CookieIdentitySessionAdapter();
const useCase = new HandleAuthCallbackUseCase(provider, sessionPort);
const command: AuthCallbackCommandDTO = params.returnTo
? {
provider: 'IRACING_DEMO',
code: params.code,
state: params.state,
returnTo: params.returnTo,
}
: {
provider: 'IRACING_DEMO',
code: params.code,
state: params.state,
};
return useCase.execute(command);
}
async logout(): Promise<void> {
const sessionPort = new CookieIdentitySessionAdapter();
const useCase = new LogoutUseCase(sessionPort);
await useCase.execute();
}
}

View File

@@ -37,33 +37,48 @@ import type { ILeagueMembershipRepository } from '@gridpilot/racing/domain/repos
import type { IFeedRepository } from '@gridpilot/social/domain/repositories/IFeedRepository';
import type { ISocialGraphRepository } from '@gridpilot/social/domain/repositories/ISocialGraphRepository';
import type { ImageServicePort } from '@gridpilot/media';
import type { ILogger } from '@gridpilot/shared/logging/ILogger';
import { ConsoleLogger } from '@gridpilot/shared/logging/ConsoleLogger';
import type { ILogger } from '@gridpilot/shared/logging';
import { ConsoleLogger } from '../../../adapters/logging';
import type { IPageViewRepository, IEngagementRepository } from '@gridpilot/analytics';
import { InMemoryPageViewRepository, InMemoryEngagementRepository } from '@gridpilot/analytics/infrastructure/repositories';
import { RecordPageViewUseCase, RecordEngagementUseCase } from '@gridpilot/analytics/application/use-cases';
import type { AuthService } from './auth/AuthService';
import { InMemoryAuthService } from './auth/InMemoryAuthService';
import type { IUserRepository, StoredUser } from '@gridpilot/identity/domain/repositories/IUserRepository';
import { InMemoryUserRepository } from '@gridpilot/identity/infrastructure/repositories/InMemoryUserRepository';
import type { ISponsorAccountRepository, SponsorAccount } from '@gridpilot/identity/domain/repositories/ISponsorAccountRepository';
import { InMemorySponsorAccountRepository } from '@gridpilot/identity/infrastructure/repositories/InMemorySponsorAccountRepository';
import type { ILiveryRepository } from '@gridpilot/racing/domain/repositories/ILiveryRepository';
import { InMemoryLiveryRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryLiveryRepository';
import type { IChampionshipStandingRepository } from '@gridpilot/racing/domain/repositories/IChampionshipStandingRepository';
import { InMemoryChampionshipStandingRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryScoringRepositories';
import type { ILeagueWalletRepository } from '@gridpilot/racing/domain/repositories/ILeagueWalletRepository';
import { InMemoryLeagueWalletRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryLeagueWalletRepository';
import type { ITransactionRepository } from '@gridpilot/racing/domain/repositories/ITransactionRepository';
import { InMemoryTransactionRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryTransactionRepository';
import type { ISessionRepository } from '@gridpilot/racing/domain/repositories/ISessionRepository';
import { InMemorySessionRepository } from '@gridpilot/racing/infrastructure/repositories/InMemorySessionRepository';
import type { IAchievementRepository } from '@gridpilot/identity/domain/repositories/IAchievementRepository';
import { InMemoryAchievementRepository } from '@gridpilot/identity/infrastructure/repositories/InMemoryAchievementRepository';
import type { IUserRatingRepository } from '@gridpilot/identity/domain/repositories/IUserRatingRepository';
import { InMemoryUserRatingRepository } from '@gridpilot/identity/infrastructure/repositories/InMemoryUserRatingRepository';
import { ConsoleLogger } from '../../../adapters/logging';
import { DI_TOKENS } from './di-tokens';
// Identity authentication use cases and ports
import { StartAuthUseCase } from '@gridpilot/identity/application/use-cases/StartAuthUseCase';
import { GetCurrentUserSessionUseCase } from '@gridpilot/identity/application/use-cases/GetCurrentUserSessionUseCase';
import { HandleAuthCallbackUseCase } from '@gridpilot/identity/application/use-cases/HandleAuthCallbackUseCase';
import { LogoutUseCase } from '@gridpilot/identity/application/use-cases/LogoutUseCase';
import { SignupWithEmailUseCase } from '@gridpilot/identity/application/use-cases/SignupWithEmailUseCase';
import { LoginWithEmailUseCase } from '@gridpilot/identity/application/use-cases/LoginWithEmailUseCase';
import type { IdentityProvider } from '@gridpilot/identity/application/ports/IdentityProvider';
import type { SessionPort } from '@gridpilot/identity/application/ports/SessionPort';
import { IracingDemoIdentityProviderAdapter } from '../../../testing/fakes/identity/IracingDemoIdentityProviderAdapter';
import { CookieIdentitySessionAdapter } from '@gridpilot/identity/infrastructure/session/CookieIdentitySessionAdapter';
import type { AuthService } from './auth/AuthService';
import type { ILogger } from '@gridpilot/shared/logging';
// Repositories
import type { IAchievementRepository } from '@gridpilot/identity/domain/repositories/IAchievementRepository';
import { InMemoryAchievementRepository } from '../../adapters/persistence/inmemory/identity/InMemoryAchievementRepository';
import type { IUserRatingRepository } from '@gridpilot/identity/domain/repositories/IUserRatingRepository';
import { InMemoryUserRatingRepository } from '../../adapters/persistence/inmemory/identity/InMemoryUserRatingRepository';
import type { ISponsorAccountRepository, SponsorAccount } from '@gridpilot/identity/domain/repositories/ISponsorAccountRepository';
import { InMemorySponsorAccountRepository } from '../../adapters/persistence/inmemory/identity/InMemorySponsorAccountRepository';
import type { IUserRepository, StoredUser } from '@gridpilot/identity/domain/repositories/IUserRepository';
import { InMemoryUserRepository } from '../../adapters/persistence/inmemory/identity/InMemoryUserRepository';
import type { ILiveryRepository } from '@gridpilot/racing/domain/repositories/ILiveryRepository';
import { InMemoryLiveryRepository } from '../../adapters/persistence/inmemory/racing/InMemoryLiveryRepository';
import type { IChampionshipStandingRepository } from '@gridpilot/racing/domain/repositories/IChampionshipStandingRepository';
import { InMemoryChampionshipStandingRepository } from '../../adapters/persistence/inmemory/racing/InMemoryScoringRepositories';
import type { ILeagueWalletRepository } from '@gridpilot/racing/domain/repositories/ILeagueWalletRepository';
import { InMemoryLeagueWalletRepository } from '../../adapters/persistence/inmemory/racing/InMemoryLeagueWalletRepository';
import type { ITransactionRepository } from '@gridpilot/racing/domain/repositories/ITransactionRepository';
import { InMemoryTransactionRepository } from '../../adapters/persistence/inmemory/racing/InMemoryTransactionRepository';
import type { ISessionRepository } from '@gridpilot/racing/domain/repositories/ISessionRepository';
import { InMemorySessionRepository } from '../../adapters/persistence/inmemory/racing/InMemorySessionRepository';
// Notifications
import type { INotificationRepository, INotificationPreferenceRepository } from '@gridpilot/notifications/application';
import {
SendNotificationUseCase,
@@ -77,10 +92,17 @@ import {
InAppNotificationAdapter,
} from '@gridpilot/notifications/infrastructure';
import type { IPageViewRepository, IEngagementRepository } from '@gridpilot/analytics';
import { InMemoryPageViewRepository, InMemoryEngagementRepository } from '../../adapters/persistence/inmemory/analytics/InMemoryAnalyticsRepositories';
import {
RecordPageViewUseCase,
RecordEngagementUseCase
} from '@gridpilot/analytics/application/use-cases';
// Infrastructure repositories
import { InMemoryDriverRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryDriverRepository';
import { InMemoryLeagueRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryLeagueRepository';
import { InMemoryRaceRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryRaceRepository';
import { InMemoryDriverRepository } from '../../adapters/persistence/inmemory/racing/InMemoryDriverRepository';
import { InMemoryLeagueRepository } from '../../adapters/persistence/inmemory/racing/InMemoryLeagueRepository';
import { InMemoryRaceRepository } from '../../adapters/persistence/inmemory/racing/InMemoryRaceRepository';
import { InMemoryResultRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryResultRepository';
import { InMemoryStandingRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryStandingRepository';
import { InMemoryPenaltyRepository } from '@gridpilot/racing/infrastructure/repositories/InMemoryPenaltyRepository';
@@ -180,6 +202,7 @@ import { LeagueStatsPresenter } from './presenters/LeagueStatsPresenter';
import { LeagueScoringConfigPresenter } from './presenters/LeagueScoringConfigPresenter';
import { LeagueFullConfigPresenter } from './presenters/LeagueFullConfigPresenter';
import { LeagueDriverSeasonStatsPresenter } from './presenters/LeagueDriverSeasonStatsPresenter';
import { LeagueStandingsPresenter as ILeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
import { LeagueStandingsPresenter } from './presenters/LeagueStandingsPresenter';
import { LeagueScoringPresetsPresenter } from './presenters/LeagueScoringPresetsPresenter';
import { RaceWithSOFPresenter } from './presenters/RaceWithSOFPresenter';
@@ -245,6 +268,10 @@ export function configureDIContainer(): void {
const driverStats: DemoDriverStatsMap = createDemoDriverStats(seedData.drivers);
// Register repositories
container.registerInstance<ILeagueStandingsRepository>(
DI_TOKENS.LeagueStandingsRepository,
new LeagueStandingsRepositoryAdapter()
);
container.registerInstance<IDriverRepository>(
DI_TOKENS.DriverRepository,
new InMemoryDriverRepository(logger, seedData.drivers)
@@ -920,6 +947,7 @@ export function configureDIContainer(): void {
const raceRegistrationRepository = container.resolve<IRaceRegistrationRepository>(DI_TOKENS.RaceRegistrationRepository);
const leagueMembershipRepository = container.resolve<ILeagueMembershipRepository>(DI_TOKENS.LeagueMembershipRepository);
const standingRepository = container.resolve<IStandingRepository>(DI_TOKENS.StandingRepository);
const leagueStandingsRepository = container.resolve<ILeagueStandingsRepository>(DI_TOKENS.LeagueStandingsRepository);
const penaltyRepository = container.resolve<IPenaltyRepository>(DI_TOKENS.PenaltyRepository);
const protestRepository = container.resolve<IProtestRepository>(DI_TOKENS.ProtestRepository);
const teamRepository = container.resolve<ITeamRepository>(DI_TOKENS.TeamRepository);
@@ -1095,10 +1123,14 @@ export function configureDIContainer(): void {
new GetRaceRegistrationsUseCase(raceRegistrationRepository)
);
const leagueStandingsPresenter = new LeagueStandingsPresenter();
container.registerInstance(
container.registerInstance<IGetLeagueStandingsUseCase>(
DI_TOKENS.GetLeagueStandingsUseCase,
new GetLeagueStandingsUseCase(standingRepository),
new GetLeagueStandingsUseCaseImpl(leagueStandingsRepository),
);
container.registerInstance<ILeagueStandingsPresenter>(
DI_TOKENS.LeagueStandingsPresenter,
new LeagueStandingsPresenter(container.resolve<IGetLeagueStandingsUseCase>(DI_TOKENS.GetLeagueStandingsUseCase)),
);
container.registerInstance(

View File

@@ -9,6 +9,7 @@ export const DI_TOKENS = {
RaceRepository: Symbol.for('IRaceRepository'),
ResultRepository: Symbol.for('IResultRepository'),
StandingRepository: Symbol.for('IStandingRepository'),
LeagueStandingsRepository: Symbol.for('ILeagueStandingsRepository'),
PenaltyRepository: Symbol.for('IPenaltyRepository'),
ProtestRepository: Symbol.for('IProtestRepository'),
TeamRepository: Symbol.for('ITeamRepository'),
@@ -50,6 +51,18 @@ export const DI_TOKENS = {
Logger: Symbol.for('ILogger'),
AuthService: Symbol.for('AuthService'),
// Auth dependencies
IdentityProvider: Symbol.for('IdentityProvider'),
SessionPort: Symbol.for('SessionPort'),
// Use Cases - Auth
StartAuthUseCase: Symbol.for('StartAuthUseCase'),
GetCurrentUserSessionUseCase: Symbol.for('GetCurrentUserSessionUseCase'),
HandleAuthCallbackUseCase: Symbol.for('HandleAuthCallbackUseCase'),
LogoutUseCase: Symbol.for('LogoutUseCase'),
SignupWithEmailUseCase: Symbol.for('SignupWithEmailUseCase'),
LoginWithEmailUseCase: Symbol.for('LoginWithEmailUseCase'),
// Use Cases - Analytics
RecordPageViewUseCase: Symbol.for('RecordPageViewUseCase'),
RecordEngagementUseCase: Symbol.for('RecordEngagementUseCase'),
@@ -141,6 +154,7 @@ export const DI_TOKENS = {
DriverStats: Symbol.for('DriverStats'),
// Presenters - Racing
LeagueStandingsPresenter: Symbol.for('ILeagueStandingsPresenter'),
RaceWithSOFPresenter: Symbol.for('IRaceWithSOFPresenter'),
RaceProtestsPresenter: Symbol.for('IRaceProtestsPresenter'),
RacePenaltiesPresenter: Symbol.for('IRacePenaltiesPresenter'),

View File

@@ -1,41 +1,22 @@
import type {
ILeagueStandingsPresenter,
LeagueStandingsResultDTO,
LeagueStandingsViewModel,
StandingItemViewModel,
} from '@gridpilot/racing/application/presenters/ILeagueStandingsPresenter';
import { GetLeagueStandingsUseCase, LeagueStandingsViewModel } from '@gridpilot/core/league/application/use-cases/GetLeagueStandingsUseCase';
export interface ILeagueStandingsPresenter {
present(leagueId: string): Promise<void>;
getViewModel(): LeagueStandingsViewModel | null;
reset(): void;
}
export class LeagueStandingsPresenter implements ILeagueStandingsPresenter {
private viewModel: LeagueStandingsViewModel | null = null;
constructor(private getLeagueStandingsUseCase: GetLeagueStandingsUseCase) {}
reset(): void {
this.viewModel = null;
}
present(dto: LeagueStandingsResultDTO): void {
const standingItems: StandingItemViewModel[] = dto.standings.map((standing) => {
const raw = standing as unknown as {
seasonId?: string;
podiums?: number;
};
return {
id: standing.id,
leagueId: standing.leagueId,
seasonId: raw.seasonId ?? '',
driverId: standing.driverId,
position: standing.position,
points: standing.points,
wins: standing.wins,
podiums: raw.podiums ?? 0,
racesCompleted: standing.racesCompleted,
};
});
this.viewModel = {
leagueId: dto.standings[0]?.leagueId ?? '',
standings: standingItems,
};
async present(leagueId: string): Promise<void> {
this.viewModel = await this.getLeagueStandingsUseCase.execute(leagueId);
}
getViewModel(): LeagueStandingsViewModel | null {

View File

@@ -28,13 +28,9 @@
"@gridpilot/testing-support/*": ["../../core/testing-support/*"],
"@gridpilot/media": ["../../core/media/index.ts"],
"@gridpilot/media/*": ["../../core/media/*"],
"@gridpilot/shared": ["../../core/shared/index.ts"],
"@gridpilot/shared/application": ["../../core/shared/application"],
"@gridpilot/shared/application/*": ["../../core/shared/application/*"],
"@gridpilot/shared/presentation": ["../../core/shared/presentation"],
"@gridpilot/shared/presentation/*": ["../../core/shared/presentation/*"],
"@gridpilot/shared/domain": ["../../core/shared/domain"],
"@gridpilot/shared/errors": ["../../core/shared/errors"]
"@gridpilot/shared/logging": ["../../core/shared/logging"],
"@gridpilot/shared/*": ["../../core/shared/*"],
"@gridpilot/core/*": ["../../core/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],