From 0303e60b64c507b03fba91f9987f024cf28c95cc Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Mon, 28 Sep 2026 12:33:56 +0300 Subject: [PATCH] style: align multiline ternary operators --- eslint.config.mjs | 6 +- src/common/filters/http-exception.filter.ts | 20 +-- .../logging/request-logging.middleware.ts | 26 +-- src/config/env.validation.ts | 6 +- .../tbank/instrument-metadata.repository.ts | 24 +-- src/integrations/tbank/tbank.client.ts | 24 +-- .../application/bank-cards.service.ts | 30 ++-- .../application/bank-operations.service.ts | 48 ++--- .../domain/bank-card.calculations.ts | 12 +- .../infrastructure/bank-cards.repository.ts | 6 +- .../brokerage-accounts.service.ts | 36 ++-- .../income/income-analytics.service.ts | 24 +-- .../investments/income/income-period.ts | 42 ++--- .../instruments/application/candle.service.ts | 68 ++++---- .../application/catalog-discovery.service.ts | 92 +++++----- .../application/catalog-enrichment.service.ts | 154 ++++++++-------- .../application/catalog-selection.ts | 12 +- .../application/instrument-market.ts | 6 +- .../application/instrument-position.ts | 22 +-- .../bonds/application/bond-catalog.service.ts | 24 +-- .../bonds/application/bond-income.service.ts | 68 ++++---- .../bonds/application/bond-market.service.ts | 28 +-- .../bonds/application/bond.spec.ts | 6 +- .../bonds/infrastructure/bond-money.mapper.ts | 6 +- .../instruments/domain/candle-coverage.ts | 6 +- .../infrastructure/candle.repository.ts | 26 +-- .../catalog-discovery.repository.ts | 66 +++---- .../application/get-stock-company.use-case.ts | 32 ++-- .../application/stock-catalog.service.ts | 26 +-- .../application/stock-income.service.ts | 50 +++--- .../application/stock-market.service.ts | 40 ++--- .../investments/investment-resource.mapper.ts | 42 ++--- .../investments/investment-sync-plan.ts | 86 ++++----- .../brokerage-operations-sync.service.ts | 20 +-- .../brokerage-operations.service.ts | 164 +++++++++--------- .../operations/domain/operations-sync-plan.ts | 30 ++-- .../brokerage-operation.mapper.ts | 22 +-- .../brokerage-operations.repository.ts | 138 +++++++-------- .../portfolio/application/forecast.service.ts | 42 ++--- .../application/portfolio.service.ts | 80 ++++----- .../portfolio/domain/portfolio-structure.ts | 14 +- .../infrastructure/forecast.repository.ts | 6 +- .../investments/tbank-sync/tbank-screen.ts | 22 +-- .../tbank-sync/tbank-sync.repository.ts | 6 +- .../tbank-sync/tbank-sync.service.ts | 146 ++++++++-------- .../upcoming-events.service.ts | 36 ++-- test/sqlite-regression.cjs | 14 +- test/sqlite.cjs | 10 +- test/tbank-ledger-local.e2e-spec.ts | 6 +- test/tbank-local-reads.e2e-spec.ts | 6 +- test/tbank-metadata-persistence.e2e-spec.ts | 6 +- test/tbank-read-baseline.e2e-spec.ts | 24 +-- test/tbank-screen-sync.e2e-spec.ts | 6 +- test/upcoming-events.e2e-spec.ts | 6 +- 54 files changed, 986 insertions(+), 982 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 02a5136..d5955f1 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -48,7 +48,11 @@ export default defineConfig( 'perfectionist/sort-classes': 'off', '@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: false }], '@stylistic/arrow-parens': ['error', 'always'], - '@stylistic/operator-linebreak': ['error', 'after'], + '@stylistic/operator-linebreak': [ + 'error', + 'after', + { overrides: { '?': 'before', ':': 'before' } }, + ], 'import-x/no-duplicates': ['error', { 'prefer-inline': true }], }, }, diff --git a/src/common/filters/http-exception.filter.ts b/src/common/filters/http-exception.filter.ts index 1e715f7..e228b30 100644 --- a/src/common/filters/http-exception.filter.ts +++ b/src/common/filters/http-exception.filter.ts @@ -15,18 +15,18 @@ export class HttpExceptionFilter implements ExceptionFilter { const context = host.switchToHttp(); const response = context.getResponse(); const status = - exception instanceof HttpException ? - exception.getStatus() : - HttpStatus.INTERNAL_SERVER_ERROR; + exception instanceof HttpException + ? exception.getStatus() + : HttpStatus.INTERNAL_SERVER_ERROR; const payload = exception instanceof HttpException ? exception.getResponse() : undefined; const isValidation = Array.isArray( (payload as { message?: unknown })?.message, ); - const code = isValidation ? - 'VALIDATION_ERROR' : - this.codeFor(status, payload); + const code = isValidation + ? 'VALIDATION_ERROR' + : this.codeFor(status, payload); response.locals ??= {}; response.locals.publicErrorCode = code; response @@ -35,13 +35,13 @@ export class HttpExceptionFilter implements ExceptionFilter { .json({ status, code, - ...(isValidation ? - { + ...(isValidation + ? { errors: this.validationErrors( (payload as { message: ValidationError[] }).message, ), - } : - {}), + } + : {}), }); } diff --git a/src/common/logging/request-logging.middleware.ts b/src/common/logging/request-logging.middleware.ts index 10fcd9b..a942167 100644 --- a/src/common/logging/request-logging.middleware.ts +++ b/src/common/logging/request-logging.middleware.ts @@ -33,26 +33,26 @@ export class RequestLoggingMiddleware implements NestMiddleware { const routePath = (routeRequest as { route?: { path?: unknown } }).route ?.path; const route = - typeof routePath === 'string' ? - `${request.baseUrl}${routePath}` : - ''; + typeof routePath === 'string' + ? `${request.baseUrl}${routePath}` + : ''; const context = { method: request.method, route, status: response.statusCode, durationMs: Math.round(performance.now() - startedAt), - ...(response.locals.publicErrorCode ? - { code: response.locals.publicErrorCode as string } : - {}), + ...(response.locals.publicErrorCode + ? { code: response.locals.publicErrorCode as string } + : {}), }; const level = - response.statusCode >= httpStatus.HTTP_STATUS_INTERNAL_SERVER_ERROR ? - 'error' : - response.statusCode === httpStatus.HTTP_STATUS_TOO_MANY_REQUESTS || - response.statusCode === httpStatus.HTTP_STATUS_UNAUTHORIZED || - response.statusCode === httpStatus.HTTP_STATUS_FORBIDDEN ? - 'warn' : - 'info'; + response.statusCode >= httpStatus.HTTP_STATUS_INTERNAL_SERVER_ERROR + ? 'error' + : response.statusCode === httpStatus.HTTP_STATUS_TOO_MANY_REQUESTS || + response.statusCode === httpStatus.HTTP_STATUS_UNAUTHORIZED || + response.statusCode === httpStatus.HTTP_STATUS_FORBIDDEN + ? 'warn' + : 'info'; this.logger.event(level, 'http.request.completed', context); }); diff --git a/src/config/env.validation.ts b/src/config/env.validation.ts index ac378db..e22b3f8 100644 --- a/src/config/env.validation.ts +++ b/src/config/env.validation.ts @@ -105,9 +105,9 @@ export function validateEnv(config: Record): Environment { JWT_SECRET: jwtSecret, PORT: parsedPort, TOKEN_ENCRYPTION_KEY: tokenEncryptionKey, - ...(typeof tbankCaCertPath === 'string' && tbankCaCertPath ? - { TBANK_CA_CERT_PATH: tbankCaCertPath } : - {}), + ...(typeof tbankCaCertPath === 'string' && tbankCaCertPath + ? { TBANK_CA_CERT_PATH: tbankCaCertPath } + : {}), TBANK_GRPC_ENDPOINT: tbankEndpoint, TBANK_RATE_LIMIT_PER_SECOND: parsedTbankRateLimit, LOG_LEVEL: logLevel as Environment['LOG_LEVEL'], diff --git a/src/integrations/tbank/instrument-metadata.repository.ts b/src/integrations/tbank/instrument-metadata.repository.ts index 277dd99..7fc6819 100644 --- a/src/integrations/tbank/instrument-metadata.repository.ts +++ b/src/integrations/tbank/instrument-metadata.repository.ts @@ -43,12 +43,12 @@ export class InstrumentMetadataRepository { async readMany(identities: MetadataIdentity[]) { const identifiers = identities.flatMap((identity) => [ - ...(identity.instrumentUid?.trim() ? - [{ identifierType: 'uid', identifier: identity.instrumentUid.trim() }] : - []), - ...(identity.figi?.trim() ? - [{ identifierType: 'figi', identifier: identity.figi.trim() }] : - []), + ...(identity.instrumentUid?.trim() + ? [{ identifierType: 'uid', identifier: identity.instrumentUid.trim() }] + : []), + ...(identity.figi?.trim() + ? [{ identifierType: 'figi', identifier: identity.figi.trim() }] + : []), ]); if (!identifiers.length) { return new Map>(); @@ -233,9 +233,9 @@ export class InstrumentMetadataRepository { update: { ...data, dataRevision: { - increment: isDeepStrictEqual(previous?.metadata, payload) ? - UNCHANGED_REVISION_INCREMENT : - REVISION_INCREMENT, + increment: isDeepStrictEqual(previous?.metadata, payload) + ? UNCHANGED_REVISION_INCREMENT + : REVISION_INCREMENT, }, }, }); @@ -256,9 +256,9 @@ export class InstrumentMetadataRepository { matchedUids, fetchedAt: now, dataRevision: { - increment: isDeepStrictEqual(previous?.matchedUids, matchedUids) ? - UNCHANGED_REVISION_INCREMENT : - REVISION_INCREMENT, + increment: isDeepStrictEqual(previous?.matchedUids, matchedUids) + ? UNCHANGED_REVISION_INCREMENT + : REVISION_INCREMENT, }, }, }); diff --git a/src/integrations/tbank/tbank.client.ts b/src/integrations/tbank/tbank.client.ts index 62101ab..3cdcfb8 100644 --- a/src/integrations/tbank/tbank.client.ts +++ b/src/integrations/tbank/tbank.client.ts @@ -205,10 +205,10 @@ export class TbankClient { loadPackageDefinition(definition) as unknown as TbankNamespace ).tinkoff.public.invest.api.contract.v1; const caPath = config.get('TBANK_CA_CERT_PATH'); - const credentials = caPath ? + const credentials = caPath // eslint-disable-next-line security/detect-non-literal-fs-filename -- The CA path is trusted operator configuration, never supplied by an HTTP request. - ChannelCredentials.createSsl(readFileSync(caPath)) : - ChannelCredentials.createSsl(); + ? ChannelCredentials.createSsl(readFileSync(caPath)) + : ChannelCredentials.createSsl(); const endpoint = config.getOrThrow('TBANK_GRPC_ENDPOINT'); const create = (name: string) => new ( @@ -497,12 +497,12 @@ export class TbankClient { return response; } catch (error) { const grpcCode = (error as ServiceError).code; - const failureContext = queueFull ? - { code: 'TBANK_UNAVAILABLE' } : - { grpcCode }; - const retryable = queueFull ? - metadataCall : - [ + const failureContext = queueFull + ? { code: 'TBANK_UNAVAILABLE' } + : { grpcCode }; + const retryable = queueFull + ? metadataCall + : [ status.DEADLINE_EXCEEDED, status.RESOURCE_EXHAUSTED, status.UNAVAILABLE, @@ -517,9 +517,9 @@ export class TbankClient { await new Promise((resolve) => setTimeout( resolve, - metadataCall ? - attempts * METADATA_RETRY_DELAY_MS : - RPC_RETRY_DELAY_MS, + metadataCall + ? attempts * METADATA_RETRY_DELAY_MS + : RPC_RETRY_DELAY_MS, ), ); continue; diff --git a/src/modules/banking/bank-cards/application/bank-cards.service.ts b/src/modules/banking/bank-cards/application/bank-cards.service.ts index 2c5d92b..7a777f9 100644 --- a/src/modules/banking/bank-cards/application/bank-cards.service.ts +++ b/src/modules/banking/bank-cards/application/bank-cards.service.ts @@ -103,17 +103,17 @@ export class BankCardsService { id, name: dto.name ?? previous.name, bankName: - dto.bankName === undefined ? - (previous.bankName ?? undefined) : - (dto.bankName ?? undefined), + dto.bankName === undefined + ? (previous.bankName ?? undefined) + : (dto.bankName ?? undefined), lastFour: - dto.lastFour === undefined ? - (previous.lastFour ?? undefined) : - (dto.lastFour ?? undefined), + dto.lastFour === undefined + ? (previous.lastFour ?? undefined) + : (dto.lastFour ?? undefined), paymentSystem: - dto.paymentSystem === undefined ? - previous.paymentSystem : - dto.paymentSystem, + dto.paymentSystem === undefined + ? previous.paymentSystem + : dto.paymentSystem, openingBalance: dto.openingBalance ?? rubMoney(previous.openingBalanceKopecks), openingDate: dto.openingDate ?? previous.openingDate, @@ -143,12 +143,12 @@ export class BankCardsService { ...input, paymentSystem: input.paymentSystem ?? null, version: { increment: 1 }, - ...(dto.archived === undefined ? - {} : - { - archivedAt: dto.archived ? - (previous.archivedAt ?? new Date()) : - null, + ...(dto.archived === undefined + ? {} + : { + archivedAt: dto.archived + ? (previous.archivedAt ?? new Date()) + : null, }), }, }); diff --git a/src/modules/banking/bank-cards/application/bank-operations.service.ts b/src/modules/banking/bank-cards/application/bank-operations.service.ts index eef0336..870748b 100644 --- a/src/modules/banking/bank-cards/application/bank-operations.service.ts +++ b/src/modules/banking/bank-cards/application/bank-operations.service.ts @@ -43,29 +43,29 @@ export class BankOperationsService { row: BankCardOperation, ): Promise { const card = await this.repository.card(tx, row.userId, row.cardId); - const target = row.targetCardId ? - await this.repository.card(tx, row.userId, row.targetCardId) : - null; - const source = row.originalExpenseId ? - await this.repository.operation(tx, row.userId, row.originalExpenseId) : - null; + const target = row.targetCardId + ? await this.repository.card(tx, row.userId, row.targetCardId) + : null; + const source = row.originalExpenseId + ? await this.repository.operation(tx, row.userId, row.originalExpenseId) + : null; const categoryId = source?.categoryId ?? row.categoryId; - const category = categoryId ? - await tx.expenseCategory.findFirst({ + const category = categoryId + ? await tx.expenseCategory.findFirst({ where: { id: categoryId, userId: row.userId }, - }) : - null; + }) + : null; const refunds = - row.kind === BankOperationKind.Expense || source ? - await tx.bankCardOperation.findMany({ + row.kind === BankOperationKind.Expense || source + ? await tx.bankCardOperation.findMany({ where: { userId: row.userId, originalExpenseId: source?.id ?? row.id, deletedAt: null, ...(source ? { id: { not: row.id } } : {}), }, - }) : - []; + }) + : []; return { id: row.id, kind: row.kind as BankOperationDto['kind'], @@ -78,12 +78,12 @@ export class BankOperationsService { originalExpenseId: row.originalExpenseId, amount: rubMoney(row.amountKopecks), availableRefund: - row.kind === BankOperationKind.Expense || source ? - rubMoney( + row.kind === BankOperationKind.Expense || source + ? rubMoney( (source?.amountKopecks ?? row.amountKopecks) - refunds.reduce((sum, r) => sum + r.amountKopecks, ZERO_KOPECKS), - ) : - null, + ) + : null, date: row.date, description: row.description, version: row.version, @@ -336,9 +336,9 @@ export class BankOperationsService { query: BankOperationsQueryDto | BankPageQueryDto, expenses = false, ) { - const period = expenses ? - undefined : - periodBounds(query as BankOperationsQueryDto); + const period = expenses + ? undefined + : periodBounds(query as BankOperationsQueryDto); return this.prisma.$transaction(async (tx) => { await this.repository.card(tx, userId, id); const limit = query.limit ?? DEFAULT_BANK_PAGE_SIZE, @@ -347,9 +347,9 @@ export class BankOperationsService { userId, deletedAt: null, OR: [{ cardId: id }, { targetCardId: id }], - ...(period ? - { date: { gte: period.from, lte: period.to } } : - { kind: BankOperationKind.Expense }), + ...(period + ? { date: { gte: period.from, lte: period.to } } + : { kind: BankOperationKind.Expense }), }; const total = await tx.bankCardOperation.count({ where }); const rows = await tx.bankCardOperation.findMany({ diff --git a/src/modules/banking/bank-cards/domain/bank-card.calculations.ts b/src/modules/banking/bank-cards/domain/bank-card.calculations.ts index cf1a0a3..cba24a7 100644 --- a/src/modules/banking/bank-cards/domain/bank-card.calculations.ts +++ b/src/modules/banking/bank-cards/domain/bank-card.calculations.ts @@ -81,13 +81,13 @@ export function bankSummary( continue; } const expense = - operation.kind === BankOperationKind.Expense ? - operation.amountKopecks : - ZERO_KOPECKS; + operation.kind === BankOperationKind.Expense + ? operation.amountKopecks + : ZERO_KOPECKS; const refund = - operation.kind === BankOperationKind.Refund ? - operation.amountKopecks : - ZERO_KOPECKS; + operation.kind === BankOperationKind.Refund + ? operation.amountKopecks + : ZERO_KOPECKS; expenses += expense; refunds += refund; if (operation.categoryId !== null) { diff --git a/src/modules/banking/bank-cards/infrastructure/bank-cards.repository.ts b/src/modules/banking/bank-cards/infrastructure/bank-cards.repository.ts index 2c5e9aa..52a54e4 100644 --- a/src/modules/banking/bank-cards/infrastructure/bank-cards.repository.ts +++ b/src/modules/banking/bank-cards/infrastructure/bank-cards.repository.ts @@ -40,9 +40,9 @@ export class BankCardsRepository { }); } catch (error) { const code = - error instanceof Prisma.PrismaClientKnownRequestError ? - error.code : - ''; + error instanceof Prisma.PrismaClientKnownRequestError + ? error.code + : ''; const busy = code === 'P2034' || code === 'P2028' || diff --git a/src/modules/investments/brokerage-accounts/brokerage-accounts.service.ts b/src/modules/investments/brokerage-accounts/brokerage-accounts.service.ts index d2c1234..8cce797 100644 --- a/src/modules/investments/brokerage-accounts/brokerage-accounts.service.ts +++ b/src/modules/investments/brokerage-accounts/brokerage-accounts.service.ts @@ -12,15 +12,15 @@ export class BrokerageAccountsService { async list(userId: string, observe = false) { const saved = await this.portfolio.saved(userId, 'all', observe); const data: BrokerageAccountDto[] | null = - saved.membership.availability === 'missing' ? - null : - saved.accounts.map((account) => ({ + saved.membership.availability === 'missing' + ? null + : saved.accounts.map((account) => ({ id: account.id, name: account.name, type: account.type as BrokerageAccountDto['type'], openedOn: account.openedOn, - portfolioSummary: account.snapshot ? - { + portfolioSummary: account.snapshot + ? { totalsByCurrency: account.snapshot.totalsByCurrency, stockPositionCount: account.snapshot.positions.filter( (position) => position.type === 'STOCK', @@ -28,8 +28,8 @@ export class BrokerageAccountsService { bondPositionCount: account.snapshot.positions.filter( (position) => position.type === 'BOND', ).length, - } : - null, + } + : null, })); return { data, @@ -47,18 +47,18 @@ export class BrokerageAccountsService { ) .digest('base64url'), availability: - data === null ? - 'missing' : - saved.accounts.some((account) => !account.snapshot) ? - 'partial' : - 'complete', - fetchedAt: saved.membership.lastSuccessfulAt ? - new Date(saved.membership.lastSuccessfulAt) : - null, + data === null + ? 'missing' + : saved.accounts.some((account) => !account.snapshot) + ? 'partial' + : 'complete', + fetchedAt: saved.membership.lastSuccessfulAt + ? new Date(saved.membership.lastSuccessfulAt) + : null, freshForMs: 300_000, - work: saved.membership.sync.id ? - { ...saved.membership.sync, id: saved.membership.sync.id } : - null, + work: saved.membership.sync.id + ? { ...saved.membership.sync, id: saved.membership.sync.id } + : null, dependencies: saved.accounts.map((account) => account.meta), }), }; diff --git a/src/modules/investments/income/income-analytics.service.ts b/src/modules/investments/income/income-analytics.service.ts index 5072d42..0c8e551 100644 --- a/src/modules/investments/income/income-analytics.service.ts +++ b/src/modules/investments/income/income-analytics.service.ts @@ -96,9 +96,9 @@ export class IncomeAnalyticsService { { ...current.meta, role: 'selected' }, { ...prior.meta, role: 'previous' }, { - ...(plan.yearAgo && year ? - year.meta : - investmentResourceMeta({ + ...(plan.yearAgo && year + ? year.meta + : investmentResourceMeta({ resourceKey: 'income/yearAgo', // gitleaks:allow public resource identifier dataRevision: 'unavailable', availability: 'missing', @@ -119,9 +119,9 @@ export class IncomeAnalyticsService { resourceKey: `income/${accountId}/${plan.selected.from}/${plan.selected.to}`, dataRevision: current.meta.dataRevision, availability: current.meta.availability, - fetchedAt: current.meta.lastSuccessfulAt ? - new Date(current.meta.lastSuccessfulAt) : - null, + fetchedAt: current.meta.lastSuccessfulAt + ? new Date(current.meta.lastSuccessfulAt) + : null, freshForMs: current.meta.freshness === 'fresh' ? Infinity : STALE_FRESHNESS_MS, work: work?.id ? { ...work, id: work.id } : null, coverage: current.meta.coverage, @@ -144,13 +144,13 @@ export class IncomeAnalyticsService { current.meta.lastSuccessfulAt, ), previous: - prior.items === null ? - null : - comparison(selected, plan.previous, qualifying(prior.items)), + prior.items === null + ? null + : comparison(selected, plan.previous, qualifying(prior.items)), yearComparison: - plan.yearAgo && year?.items !== null && year ? - comparison(selected, plan.yearAgo, qualifying(year.items)) : - null, + plan.yearAgo && year?.items !== null && year + ? comparison(selected, plan.yearAgo, qualifying(year.items)) + : null, bucketGranularity: plan.granularity, buckets: buckets(plan.selected, plan.granularity, selected), instruments: instruments(selected), diff --git a/src/modules/investments/income/income-period.ts b/src/modules/investments/income/income-period.ts index 087d39e..32f91699 100644 --- a/src/modules/investments/income/income-period.ts +++ b/src/modules/investments/income/income-period.ts @@ -39,24 +39,24 @@ export function planIncomePeriod( const selected = from && to ? { from, to } : { from: firstOfMonth(today), to: today }; assertRange(selected, today); - const previous = isFullMonth(selected) ? - previousMonth(selected.from) : - shiftDays(selected, -rangeDays(selected)); + const previous = isFullMonth(selected) + ? previousMonth(selected.from) + : shiftDays(selected, -rangeDays(selected)); const candidate = yearAgo(selected); const yearAgoRange = - candidate && (!openedDate || candidate.from >= moscowDate(openedDate)) ? - candidate : - null; + candidate && (!openedDate || candidate.from >= moscowDate(openedDate)) + ? candidate + : null; return { selected, previous, yearAgo: yearAgoRange, granularity: - rangeDays(selected) <= DAILY_BUCKET_MAX_DAYS ? - 'day' : - rangeDays(selected) <= WEEKLY_BUCKET_MAX_DAYS ? - 'week' : - 'month', + rangeDays(selected) <= DAILY_BUCKET_MAX_DAYS + ? 'day' + : rangeDays(selected) <= WEEKLY_BUCKET_MAX_DAYS + ? 'week' + : 'month', }; } @@ -69,20 +69,20 @@ export function rangeBuckets( } const buckets: DateOnlyRange[] = []; let cursor = - granularity === 'week' ? - addDays( + granularity === 'week' + ? addDays( range.from, -( (utcDate(range.from).getUTCDay() + DAYS_PER_WEEK - MONDAY_WEEKDAY_INDEX) % DAYS_PER_WEEK ), - ) : - `${range.from.slice(ISO_DATE_START, ISO_MONTH_LENGTH)}-01`; + ) + : `${range.from.slice(ISO_DATE_START, ISO_MONTH_LENGTH)}-01`; while (cursor <= range.to) { const end = - granularity === 'week' ? - addDays(cursor, DAYS_PER_WEEK - INCLUSIVE_DAY_OFFSET) : - endOfMonth(cursor); + granularity === 'week' + ? addDays(cursor, DAYS_PER_WEEK - INCLUSIVE_DAY_OFFSET) + : endOfMonth(cursor); buckets.push({ from: cursor < range.from ? range.from : cursor, to: end > range.to ? range.to : end, @@ -153,9 +153,9 @@ function yearAgo(range: DateOnlyRange): DateOnlyRange | null { const shift = (value: string) => { const [year, month, day] = dateParts(value); const result = `${String(year - PREVIOUS_YEAR_OFFSET).padStart(ISO_YEAR_LENGTH, '0')}-${String(month).padStart(DATE_COMPONENT_DIGITS, '0')}-${String(day).padStart(DATE_COMPONENT_DIGITS, '0')}`; - return utcDate(result).toISOString().slice(ISO_DATE_START, ISO_DATE_LENGTH) === result ? - result : - null; + return utcDate(result).toISOString().slice(ISO_DATE_START, ISO_DATE_LENGTH) === result + ? result + : null; }; const from = shift(range.from); const to = shift(range.to); diff --git a/src/modules/investments/instruments/application/candle.service.ts b/src/modules/investments/instruments/application/candle.service.ts index 5e17cdc..9566d4e 100644 --- a/src/modules/investments/instruments/application/candle.service.ts +++ b/src/modules/investments/instruments/application/candle.service.ts @@ -70,9 +70,9 @@ function rangeFor( if (!history) { throw new ServiceUnavailableException({ code: - input.instrumentType === 'share' ? - 'STOCK_HISTORY_START_UNAVAILABLE' : - 'BOND_HISTORY_START_UNAVAILABLE', + input.instrumentType === 'share' + ? 'STOCK_HISTORY_START_UNAVAILABLE' + : 'BOND_HISTORY_START_UNAVAILABLE', }); } from.setTime(utcDay(new Date(history)).valueOf()); @@ -114,9 +114,9 @@ export class CandleService { throw new NotFoundException(); } const now = new Date(); - const range = detail.instrument ? - rangeFor(input, detail.instrument, now) : - null; + const range = detail.instrument + ? rangeFor(input, detail.instrument, now) + : null; const saved = range ? await this.repository.read(input.uid, range) : null; const gaps = range && saved ? candleGaps(range, saved.coverage, now) : []; if (!observe && (!saved || gaps.length)) { @@ -139,14 +139,14 @@ export class CandleService { }); return { data: - !saved || !covered ? - null : - { + !saved || !covered + ? null + : { candles: saved.data, currency: - input.instrumentType === 'share' ? - detail.instrument?.currency?.toUpperCase() || null : - null, + input.instrumentType === 'share' + ? detail.instrument?.currency?.toUpperCase() || null + : null, historyFrom: timestamp(detail?.instrument?.first_1dayCandleDate), observedAt: fetchedAt?.toISOString() ?? now.toISOString(), }, @@ -159,8 +159,8 @@ export class CandleService { work, coverage: { requested: range ? [publicRange(range)] : [], - completeRanges: range ? - completeCoverage.flatMap((row) => { + completeRanges: range + ? completeCoverage.flatMap((row) => { const from = new Date( Math.max(row.from.valueOf(), range.from.valueOf()), ); @@ -168,22 +168,22 @@ export class CandleService { Math.min(row.to.valueOf(), range.to.valueOf()), ); return from < to ? [publicRange({ from, to })] : []; - }) : - [], + }) + : [], missingRanges: missing.map(publicRange), asOf: fetchedAt?.toISOString() ?? null, - historyBoundary: timestamp(detail?.instrument?.first_1dayCandleDate) ? - 'known' : - 'unknown', - historyState: complete ? - 'complete' : - work?.state === 'failed' ? - 'failed' : - work?.state === 'running' ? - 'loading' : - covered ? - 'partial' : - 'missing', + historyBoundary: timestamp(detail?.instrument?.first_1dayCandleDate) + ? 'known' + : 'unknown', + historyState: complete + ? 'complete' + : work?.state === 'failed' + ? 'failed' + : work?.state === 'running' + ? 'loading' + : covered + ? 'partial' + : 'missing', }, dependencies: [ detail.meta, @@ -193,9 +193,9 @@ export class CandleService { dataRevision: String(row.dataRevision), availability: 'complete', fetchedAt: row.fetchedAt, - freshForMs: row.isComplete ? - Number.POSITIVE_INFINITY : - tailFreshForMs, + freshForMs: row.isComplete + ? Number.POSITIVE_INFINITY + : tailFreshForMs, work: null, }), ), @@ -291,9 +291,9 @@ export class CandleService { ) { const input = { uid, instrumentType, period: 'all' as const }; const range = - detail?.instrument && history ? - rangeFor(input, detail.instrument, now) : - null; + detail?.instrument && history + ? rangeFor(input, detail.instrument, now) + : null; if ( !range || candleGaps( diff --git a/src/modules/investments/instruments/application/catalog-discovery.service.ts b/src/modules/investments/instruments/application/catalog-discovery.service.ts index 54dc369..6105b3a 100644 --- a/src/modules/investments/instruments/application/catalog-discovery.service.ts +++ b/src/modules/investments/instruments/application/catalog-discovery.service.ts @@ -79,14 +79,14 @@ export class CatalogDiscoveryService { async search(userId: string, input: CatalogQuery, observe = false) { const discovery = await this.read(userId, input, observe); - const sources = discovery.data ? - await this.enrichment.readMany( + const sources = discovery.data + ? await this.enrichment.readMany( userId, input.instrumentType, discovery.data.map((item) => item.uid), observe, - ) : - []; + ) + : []; const dependencies = [ discovery.meta, ...sources.map((source) => source.meta), @@ -114,22 +114,22 @@ export class CatalogDiscoveryService { .map((dependency) => dependency.dataRevision) .join('/'), availability: - discovery.data === null ? - 'missing' : - sources.every((source) => source.meta.availability === 'complete') ? - 'complete' : - 'partial', - fetchedAt: discovery.meta.lastSuccessfulAt ? - new Date(discovery.meta.lastSuccessfulAt) : - null, + discovery.data === null + ? 'missing' + : sources.every((source) => source.meta.availability === 'complete') + ? 'complete' + : 'partial', + fetchedAt: discovery.meta.lastSuccessfulAt + ? new Date(discovery.meta.lastSuccessfulAt) + : null, freshForMs: tbankSourceTtlMs.catalog, - work: discovery.meta.sync.id ? - { + work: discovery.meta.sync.id + ? { id: discovery.meta.sync.id, state: discovery.meta.sync.state, errorCode: discovery.meta.sync.errorCode, - } : - null, + } + : null, dependencies, }), }; @@ -177,17 +177,17 @@ export class CatalogDiscoveryService { .map((dependency) => dependency.dataRevision) .join('/'), availability: source.meta.availability, - fetchedAt: source.meta.lastSuccessfulAt ? - new Date(source.meta.lastSuccessfulAt) : - null, + fetchedAt: source.meta.lastSuccessfulAt + ? new Date(source.meta.lastSuccessfulAt) + : null, freshForMs: tbankSourceTtlMs.metadata, - work: source.meta.sync.id ? - { + work: source.meta.sync.id + ? { id: source.meta.sync.id, state: source.meta.sync.state, errorCode: source.meta.sync.errorCode, - } : - null, + } + : null, dependencies, }), }; @@ -208,36 +208,36 @@ export class CatalogDiscoveryService { if (lookup.data && lookup.data.length > UNAMBIGUOUS_MATCH_COUNT) { throw new ConflictException({ code: - instrumentType === 'share' ? - 'STOCK_TICKER_AMBIGUOUS' : - 'BOND_TICKER_AMBIGUOUS', + instrumentType === 'share' + ? 'STOCK_TICKER_AMBIGUOUS' + : 'BOND_TICKER_AMBIGUOUS', }); } - const result = lookup.data ? - await this.detail(userId, instrumentType, lookup.data[0].uid, observe) : - null; + const result = lookup.data + ? await this.detail(userId, instrumentType, lookup.data[0].uid, observe) + : null; const dependencies = [lookup.meta, ...(result ? [result.meta] : [])]; return { - data: result?.data ? - { ...result.data, slug: ticker.trim().toUpperCase() } : - null, + data: result?.data + ? { ...result.data, slug: ticker.trim().toUpperCase() } + : null, meta: investmentResourceMeta({ resourceKey: `${instrumentType}/ticker/${ticker.trim().toUpperCase()}`, dataRevision: dependencies .map((dependency) => dependency.dataRevision) .join('/'), availability: result?.meta.availability ?? 'missing', - fetchedAt: lookup.meta.lastSuccessfulAt ? - new Date(lookup.meta.lastSuccessfulAt) : - null, + fetchedAt: lookup.meta.lastSuccessfulAt + ? new Date(lookup.meta.lastSuccessfulAt) + : null, freshForMs: tbankSourceTtlMs.metadata, - work: lookup.meta.sync.id ? - { + work: lookup.meta.sync.id + ? { id: lookup.meta.sync.id, state: lookup.meta.sync.state, errorCode: lookup.meta.sync.errorCode, - } : - null, + } + : null, dependencies, }), }; @@ -334,19 +334,19 @@ export class CatalogDiscoveryService { const token = await this.access.tokenForAttempt(attempt); if (token) { const found = - input.instrumentType === 'share' ? - await this.tbank.findShares(token, input.query) : - await this.tbank.findBonds(token, input.query); + input.instrumentType === 'share' + ? await this.tbank.findShares(token, input.query) + : await this.tbank.findBonds(token, input.query); success = await this.repository.publish( attempt, input, selectCatalogInstruments( - input.exactTicker ? - found.filter( + input.exactTicker + ? found.filter( (item) => item.ticker?.trim().toUpperCase() === input.query, - ) : - found, + ) + : found, input.query, input.instrumentType, ).slice( diff --git a/src/modules/investments/instruments/application/catalog-enrichment.service.ts b/src/modules/investments/instruments/application/catalog-enrichment.service.ts index edbed0c..1b8e892 100644 --- a/src/modules/investments/instruments/application/catalog-enrichment.service.ts +++ b/src/modules/investments/instruments/application/catalog-enrichment.service.ts @@ -124,9 +124,9 @@ export class CatalogEnrichmentService { range, ); } - const work = inputs.length ? - await this.repository.workMany(userId, revision, inputs) : - []; + const work = inputs.length + ? await this.repository.workMany(userId, revision, inputs) + : []; const byScope = new Map(work.map((row) => [row.scopeKey, row])); return inputs.map((input) => { const row = rows.find( @@ -136,16 +136,16 @@ export class CatalogEnrichmentService { const payload = row?.payload as CatalogPayload | undefined; return { input, - version: row ? - { + version: row + ? { instrumentUid: row.instrumentUid, source: row.source, kind: row.kind, rangeKey: row.rangeKey, dataRevision: row.dataRevision, fetchedAt: row.fetchedAt, - } : - null, + } + : null, payload, instrument: payload?.instrument ?? null, price: payload?.price ?? null, @@ -177,9 +177,9 @@ export class CatalogEnrichmentService { ) { const inputs = sourceInputs(instrumentType, uids, kinds, range); const reservation = this.reservations.then(async () => { - const rows = inputs.length ? - await this.repository.readSources(inputs) : - []; + const rows = inputs.length + ? await this.repository.readSources(inputs) + : []; const ids: string[] = []; for (const input of inputs) { const row = rows.find( @@ -242,18 +242,18 @@ export class CatalogEnrichmentService { observe = false, ) { const kinds: CatalogSource['kind'][] = - section === 'company' ? - ['detail', 'description', 'fundamentals'] : - [ - ...(instrumentType === 'bond' ? - ['bond-current' as const] : - ['detail' as const]), + section === 'company' + ? ['detail', 'description', 'fundamentals'] + : [ + ...(instrumentType === 'bond' + ? ['bond-current' as const] + : ['detail' as const]), 'last-price', 'trading-status', 'comparison', - ...(section === 'metrics' ? - ['coupons' as const, 'yield' as const, 'rating' as const] : - []), + ...(section === 'metrics' + ? ['coupons' as const, 'yield' as const, 'rating' as const] + : []), ]; const sources = await this.readMany( userId, @@ -279,18 +279,18 @@ export class CatalogEnrichmentService { dataRevision: dependencies .map((dependency) => dependency.dataRevision) .join('/'), - availability: !first.instrument ? - 'missing' : - complete ? - 'complete' : - 'partial', - fetchedAt: first.meta.lastSuccessfulAt ? - new Date(first.meta.lastSuccessfulAt) : - null, + availability: !first.instrument + ? 'missing' + : complete + ? 'complete' + : 'partial', + fetchedAt: first.meta.lastSuccessfulAt + ? new Date(first.meta.lastSuccessfulAt) + : null, freshForMs: - section === 'company' ? - tbankSourceTtlMs.company : - tbankSourceTtlMs.quote, + section === 'company' + ? tbankSourceTtlMs.company + : tbankSourceTtlMs.quote, work: null, dependencies, }); @@ -315,9 +315,9 @@ export class CatalogEnrichmentService { ): Promise { if (input.kind === 'detail' || input.kind === 'bond-current') { const instrument = - input.instrumentType === 'share' ? - await this.tbank.getShare(token, input.uid) : - await this.tbank.getBond(token, input.uid); + input.instrumentType === 'share' + ? await this.tbank.getShare(token, input.uid) + : await this.tbank.getBond(token, input.uid); if (instrument && instrument.uid !== input.uid) { throw new Error('Catalog UID mismatch'); } @@ -400,9 +400,9 @@ export class CatalogEnrichmentService { } const details = await this.repository.readSources([ { ...input, kind: 'detail', rangeKey: undefined }, - ...(input.instrumentType === 'bond' ? - [{ ...input, kind: 'bond-current' as const, rangeKey: undefined }] : - []), + ...(input.instrumentType === 'bond' + ? [{ ...input, kind: 'bond-current' as const, rangeKey: undefined }] + : []), ]); const instrument = details .map((row) => (row.payload as CatalogPayload).instrument) @@ -415,36 +415,36 @@ export class CatalogEnrichmentService { } if (input.kind === 'description') { return { - description: instrument?.assetUid ? - await this.tbank.getAssetDescription(token, instrument?.assetUid) : - null, + description: instrument?.assetUid + ? await this.tbank.getAssetDescription(token, instrument?.assetUid) + : null, }; } if (input.kind === 'fundamentals') { return { - fundamentals: instrument?.assetUid ? - await this.tbank.getFundamentals(token, instrument?.assetUid) : - null, + fundamentals: instrument?.assetUid + ? await this.tbank.getFundamentals(token, instrument?.assetUid) + : null, }; } if (input.kind === 'coupons' || input.kind === 'dividends') { const year = Number(input.rangeKey); - const [from, to] = input.rangeKey?.startsWith('[') ? - (JSON.parse(input.rangeKey) as [string, string]) : - [ + const [from, to] = input.rangeKey?.startsWith('[') + ? (JSON.parse(input.rangeKey) as [string, string]) + : [ `${year - payoutHistoryYears}-01-01`, `${year + payoutFutureYears}-01-01`, ]; - return input.kind === 'coupons' ? - { + return input.kind === 'coupons' + ? { coupons: await this.tbank.getBondCoupons( token, input.uid, from, to, ), - } : - { + } + : { dividends: await this.tbank.getDividends( token, input.uid, @@ -461,20 +461,20 @@ export class CatalogEnrichmentService { if (input.kind === 'yield') { return { effectiveYield: - instrument?.ticker && instrument.isin && instrument.classCode ? - await this.moex.bondYield( + instrument?.ticker && instrument.isin && instrument.classCode + ? await this.moex.bondYield( instrument.ticker, instrument.isin, instrument.classCode, - ) : - null, + ) + : null, }; } if (input.kind === 'rating') { return { - creditRating: instrument?.isin ? - await this.ratings.rating(instrument.isin) : - null, + creditRating: instrument?.isin + ? await this.ratings.rating(instrument.isin) + : null, }; } throw new Error('Unsupported catalog source'); @@ -533,17 +533,17 @@ export class CatalogEnrichmentService { prices?.find((entry) => entry.instrumentUid === item.input.uid) ?? null; const value = - item.input.kind === 'last-price' ? - { - price: price ? - { + item.input.kind === 'last-price' + ? { + price: price + ? { instrumentUid: item.input.uid, price: price.price, time: price.time, - } : - null, - } : - payload; + } + : null, + } + : payload; // Keep accepting observers while publication awaits SQLite. item.observers.unshift({ attempt: item.attempt, input: item.input }); for (const observer of item.observers) { @@ -596,17 +596,17 @@ function sourceInputs( uid, kind, // eslint-disable-next-line security/detect-object-injection -- Numeric queue indexes and validated instrument UIDs select internal data only. - ...(kind === 'bond-events' && (range?.tickers?.[uid] || range?.ticker) ? + ...(kind === 'bond-events' && (range?.tickers?.[uid] || range?.ticker) // eslint-disable-next-line security/detect-object-injection -- Numeric queue indexes and validated instrument UIDs select internal data only. - { ticker: range?.tickers?.[uid] || range?.ticker } : - {}), - ...(['coupons', 'dividends'].includes(kind) ? - { - rangeKey: range ? - JSON.stringify([range.from, range.to]) : - String(new Date().getUTCFullYear()), - } : - {}), + ? { ticker: range?.tickers?.[uid] || range?.ticker } + : {}), + ...(['coupons', 'dividends'].includes(kind) + ? { + rangeKey: range + ? JSON.stringify([range.from, range.to]) + : String(new Date().getUTCFullYear()), + } + : {}), })), ); } @@ -679,9 +679,9 @@ function sourcePolicy(kind: CatalogSource['kind']) { } function producerKey(input: CatalogSource) { return input.instrumentType === 'bond' && - (input.kind === 'detail' || input.kind === 'bond-current') ? - `bond-metadata:${input.uid}` : - catalogScope(input); + (input.kind === 'detail' || input.kind === 'bond-current') + ? `bond-metadata:${input.uid}` + : catalogScope(input); } const quotationScale = 9; diff --git a/src/modules/investments/instruments/application/catalog-selection.ts b/src/modules/investments/instruments/application/catalog-selection.ts index c91f26a..2928a9e 100644 --- a/src/modules/investments/instruments/application/catalog-selection.ts +++ b/src/modules/investments/instruments/application/catalog-selection.ts @@ -9,9 +9,9 @@ export function selectCatalogInstruments( ): (T & { uid: string })[] { const seen = new Set(); const kind = - instrumentType === 'share' ? - 'INSTRUMENT_TYPE_SHARE' : - 'INSTRUMENT_TYPE_BOND'; + instrumentType === 'share' + ? 'INSTRUMENT_TYPE_SHARE' + : 'INSTRUMENT_TYPE_BOND'; const preferredClass = instrumentType === 'share' ? 'TQBR' : 'TQOB'; return found .filter( @@ -29,9 +29,9 @@ export function selectCatalogInstruments( a.uid.localeCompare(b.uid), ) .filter((instrument) => { - const key = instrument.isin?.trim() ? - `isin:${instrument.isin.trim().toUpperCase()}` : - `uid:${instrument.uid}`; + const key = instrument.isin?.trim() + ? `isin:${instrument.isin.trim().toUpperCase()}` + : `uid:${instrument.uid}`; if (seen.has(key)) return false; seen.add(key); return true; diff --git a/src/modules/investments/instruments/application/instrument-market.ts b/src/modules/investments/instruments/application/instrument-market.ts index 4620119..53f9c52 100644 --- a/src/modules/investments/instruments/application/instrument-market.ts +++ b/src/modules/investments/instruments/application/instrument-market.ts @@ -10,9 +10,9 @@ const MIN_TIMESTAMP_SECONDS_EXCLUSIVE = 0; const MOSCOW_OFFSET_MS = 10_800_000; export function timestamp(value?: TbankTimestamp): null | string { const seconds = Number(value?.seconds); - return Number.isFinite(seconds) && seconds > MIN_TIMESTAMP_SECONDS_EXCLUSIVE ? - new Date(seconds * MILLISECONDS_PER_SECOND).toISOString() : - null; + return Number.isFinite(seconds) && seconds > MIN_TIMESTAMP_SECONDS_EXCLUSIVE + ? new Date(seconds * MILLISECONDS_PER_SECOND).toISOString() + : null; } export function moscowDay(iso: string): string { return new Date(Date.parse(iso) + MOSCOW_OFFSET_MS) diff --git a/src/modules/investments/instruments/application/instrument-position.ts b/src/modules/investments/instruments/application/instrument-position.ts index 9fdb930..6bbc2cb 100644 --- a/src/modules/investments/instruments/application/instrument-position.ts +++ b/src/modules/investments/instruments/application/instrument-position.ts @@ -43,8 +43,8 @@ export function instrumentPosition( currentValue, absoluteReturn, averagePurchasePrice: - positive && totalCost ? - { + positive && totalCost + ? { currency: totalCost.currency, value: formatDecimal( divide( @@ -53,15 +53,15 @@ export function instrumentPosition( PRICE_DECIMAL_PLACES, ), ), - } : - null, + } + : null, returnPercent: positive ? percent(absoluteReturn, totalCost) : null, accounts: positions.map((p) => ({ ...p, returnPercent: - decimalFromString(p.quantity).value > ZERO_QUANTITY ? - percent(p.absoluteReturn, cost(p)) : - null, + decimalFromString(p.quantity).value > ZERO_QUANTITY + ? percent(p.absoluteReturn, cost(p)) + : null, })), isStale: portfolio.isStale, observedAt: portfolio.lastSuccessfulAt, @@ -69,8 +69,8 @@ export function instrumentPosition( } function cost(position: PortfolioPositionDto): MoneyDto | null { const price = position.averagePurchasePrice; - return price ? - { + return price + ? { currency: price.currency, value: formatDecimal( multiply( @@ -78,8 +78,8 @@ function cost(position: PortfolioPositionDto): MoneyDto | null { decimalFromString(position.quantity), ), ), - } : - null; + } + : null; } function sumComplete(values: (MoneyDto | null)[]): MoneyDto | null { const currency = values[0]?.currency; diff --git a/src/modules/investments/instruments/bonds/application/bond-catalog.service.ts b/src/modules/investments/instruments/bonds/application/bond-catalog.service.ts index df5972b..dbfcee5 100644 --- a/src/modules/investments/instruments/bonds/application/bond-catalog.service.ts +++ b/src/modules/investments/instruments/bonds/application/bond-catalog.service.ts @@ -37,18 +37,18 @@ export class BondCatalogService { data: result.data?.map((item) => { const short = toBond(item); - const detail = item.detail ? - toBond({ ...item.detail, uid: item.uid }) : - short; + const detail = item.detail + ? toBond({ ...item.detail, uid: item.uid }) + : short; const last = item.price; return { ...detail, classCode: detail.classCode ?? short.classCode, isin: detail.isin ?? short.isin, apiTradeAvailable: short.apiTradeAvailable, - pricePercent: last?.price ? - formatDecimal(decimal(last.price)) : - null, + pricePercent: last?.price + ? formatDecimal(decimal(last.price)) + : null, priceAt: timestamp(last?.time), }; }) ?? null, @@ -63,9 +63,9 @@ export class BondCatalogService { const result = await this.discovery.detail(userId, 'bond', uid, observe); return { ...result, - data: result.data ? - { ...toBond(result.data), slug: result.data.slug } : - null, + data: result.data + ? { ...toBond(result.data), slug: result.data.slug } + : null, }; } @@ -82,9 +82,9 @@ export class BondCatalogService { ); return { ...result, - data: result.data ? - { ...toBond(result.data), slug: result.data.slug } : - null, + data: result.data + ? { ...toBond(result.data), slug: result.data.slug } + : null, }; } } diff --git a/src/modules/investments/instruments/bonds/application/bond-income.service.ts b/src/modules/investments/instruments/bonds/application/bond-income.service.ts index 6096373..ab09102 100644 --- a/src/modules/investments/instruments/bonds/application/bond-income.service.ts +++ b/src/modules/investments/instruments/bonds/application/bond-income.service.ts @@ -62,9 +62,9 @@ export class BondIncomeService { observe, ); return { - data: result.values.instrument ? - bondMetrics(result.values, result.unavailable, result.observedAt) : - null, + data: result.values.instrument + ? bondMetrics(result.values, result.unavailable, result.observedAt) + : null, meta: result.meta, }; } @@ -99,18 +99,18 @@ export class BondIncomeService { throw new NotFoundException(); } const events = sources[1].payload?.coupons; - const position = portfolio.data ? - instrumentPosition(portfolio.data, uid, 'BOND') : - null; - const projected = events ? - bondCoupons( + const position = portfolio.data + ? instrumentPosition(portfolio.data, uid, 'BOND') + : null; + const projected = events + ? bondCoupons( events, position, { from, to, today }, sources[1].meta.lastSuccessfulAt ?? new Date().toISOString(), !portfolio.data, - ) : - undefined; + ) + : undefined; const result = await this.forecasts.result({ userId, revision: connection.revision, @@ -121,9 +121,9 @@ export class BondIncomeService { candidate: portfolio.data && sources[0].instrument ? projected : undefined, }); - return result.data || !projected ? - result : - { + return result.data || !projected + ? result + : { data: bondCoupons( events ?? [], null, @@ -146,19 +146,19 @@ export function bondCoupons( const { from, to, today } = window; const quantity = position?.accounts.length && - position.accounts.every((p) => decimalFromString(p.quantity).value > ZERO_QUANTITY) ? - decimalFromString(position.quantity) : - null; + position.accounts.every((p) => decimalFromString(p.quantity).value > ZERO_QUANTITY) + ? decimalFromString(position.quantity) + : null; const coupons = events .map((event) => { const paymentDate = timestamp(event.couponDate)?.slice(ISO_DATE_START, ISO_DATE_LENGTH) ?? null; const recordDate = timestamp(event.fixDate)?.slice(ISO_DATE_START, ISO_DATE_LENGTH) ?? null; const perBond = money(event.payOneBond); - const timing = !paymentDate ? - ('undated' as const) : - paymentDate >= today ? - ('upcoming' as const) : - ('past' as const); + const timing = !paymentDate + ? ('undated' as const) + : paymentDate >= today + ? ('upcoming' as const) + : ('past' as const); return { number: event.couponNumber ?? null, paymentDate, @@ -172,14 +172,14 @@ export function bondCoupons( perBond && timing === 'upcoming' && recordDate && - recordDate >= today ? - { + recordDate >= today + ? { currency: perBond.currency, value: formatDecimal( multiply(quantity, decimalFromString(perBond.value)), ), - } : - null, + } + : null, }; }) .sort((a, b) => (a.paymentDate ?? '').localeCompare(b.paymentDate ?? '')); @@ -225,8 +225,8 @@ export function bondMetrics( clean && coupon.currency === clean.currency && decimalFromString(coupon.value).value >= ZERO_AMOUNT && - decimalFromString(clean.value).value > ZERO_AMOUNT ? - formatDecimal( + decimalFromString(clean.value).value > ZERO_AMOUNT + ? formatDecimal( divide( multiply( decimalFromString(coupon.value), @@ -238,14 +238,14 @@ export function bondMetrics( ), COUPON_YIELD_DECIMAL_PLACES, ), - ) : - null; + ) + : null; const couponType = - next?.couponType && next.couponType !== 'COUPON_TYPE_UNSPECIFIED' ? - next.couponType : - instrument.floatingCouponFlag === true ? - 'COUPON_TYPE_FLOATING' : - null; + next?.couponType && next.couponType !== 'COUPON_TYPE_UNSPECIFIED' + ? next.couponType + : instrument.floatingCouponFlag === true + ? 'COUPON_TYPE_FLOATING' + : null; return { creditRating, coupon, diff --git a/src/modules/investments/instruments/bonds/application/bond-market.service.ts b/src/modules/investments/instruments/bonds/application/bond-market.service.ts index fbb4fcf..5902ea8 100644 --- a/src/modules/investments/instruments/bonds/application/bond-market.service.ts +++ b/src/modules/investments/instruments/bonds/application/bond-market.service.ts @@ -48,9 +48,9 @@ export class BondMarketService { observe, ); return { - data: result.values.instrument ? - bondQuote(result.values, result.unavailable, result.observedAt) : - null, + data: result.values.instrument + ? bondQuote(result.values, result.unavailable, result.observedAt) + : null, meta: result.meta, }; } @@ -83,8 +83,8 @@ export function bondQuote( const cleanPrice = pricePercent !== null && nominal && - decimalFromString(nominal.value).value > ZERO_AMOUNT ? - { + decimalFromString(nominal.value).value > ZERO_AMOUNT + ? { currency: nominal.currency, value: formatDecimal( divide( @@ -96,11 +96,11 @@ export function bondQuote( PRICE_DECIMAL_PLACES, ), ), - } : - null; + } + : null; const dirtyPrice = - cleanPrice && aci && aci.currency === cleanPrice.currency ? - { + cleanPrice && aci && aci.currency === cleanPrice.currency + ? { currency: cleanPrice.currency, value: formatDecimal( add( @@ -108,8 +108,8 @@ export function bondQuote( decimalFromString(aci.value), ), ), - } : - null; + } + : null; return { pricePercent, cleanPrice, @@ -118,9 +118,9 @@ export function bondQuote( priceAt, tradingStatus, changePercentagePoints: - last?.price && previous?.close ? - formatDecimal(subtract(decimal(last.price), decimal(previous.close))) : - null, + last?.price && previous?.close + ? formatDecimal(subtract(decimal(last.price), decimal(previous.close))) + : null, previousCloseDate: timestamp(previous?.time)?.slice(ISO_DATE_START, ISO_DATE_LENGTH) ?? null, unavailable, observedAt, diff --git a/src/modules/investments/instruments/bonds/application/bond.spec.ts b/src/modules/investments/instruments/bonds/application/bond.spec.ts index 5f9098b..fed812e 100644 --- a/src/modules/investments/instruments/bonds/application/bond.spec.ts +++ b/src/modules/investments/instruments/bonds/application/bond.spec.ts @@ -123,9 +123,9 @@ describe('bond financial projections', () => { { couponDate: time('2026-10-10'), payOneBond: - scenario === 'unknown' ? - undefined : - money('30', scenario === 'currency' ? 'usd' : 'rub'), + scenario === 'unknown' + ? undefined + : money('30', scenario === 'currency' ? 'usd' : 'rub'), couponPeriod: scenario === 'period' ? 0 : 90, }, ], diff --git a/src/modules/investments/instruments/bonds/infrastructure/bond-money.mapper.ts b/src/modules/investments/instruments/bonds/infrastructure/bond-money.mapper.ts index 861a30b..fd115e7 100644 --- a/src/modules/investments/instruments/bonds/infrastructure/bond-money.mapper.ts +++ b/src/modules/investments/instruments/bonds/infrastructure/bond-money.mapper.ts @@ -4,7 +4,7 @@ import { type TbankMoney } from '../../../../../integrations/tbank/tbank.api'; export function money(value?: TbankMoney) { const currency = value?.currency?.toUpperCase(); - return value && currency ? - { currency, value: formatDecimal(decimal(value)) } : - null; + return value && currency + ? { currency, value: formatDecimal(decimal(value)) } + : null; } diff --git a/src/modules/investments/instruments/domain/candle-coverage.ts b/src/modules/investments/instruments/domain/candle-coverage.ts index 3db1224..ef2dad1 100644 --- a/src/modules/investments/instruments/domain/candle-coverage.ts +++ b/src/modules/investments/instruments/domain/candle-coverage.ts @@ -38,9 +38,9 @@ export function candleGaps( gaps = gaps.flatMap((gap) => { if (covered.to <= gap.from || covered.from >= gap.to) return [gap]; return [ - ...(covered.from > gap.from ? - [{ from: gap.from, to: covered.from }] : - []), + ...(covered.from > gap.from + ? [{ from: gap.from, to: covered.from }] + : []), ...(covered.to < gap.to ? [{ from: covered.to, to: gap.to }] : []), ]; }); diff --git a/src/modules/investments/instruments/infrastructure/candle.repository.ts b/src/modules/investments/instruments/infrastructure/candle.repository.ts index c9750cf..844916f 100644 --- a/src/modules/investments/instruments/infrastructure/candle.repository.ts +++ b/src/modules/investments/instruments/infrastructure/candle.repository.ts @@ -100,14 +100,14 @@ export class CandleRepository { priority: nextPriority, intent: { ...previousIntent, - ...(intent.force ? - { + ...(intent.force + ? { force: true, - ...(intent.now && intent.now > previousNow ? - { now: intent.now } : - {}), - } : - {}), + ...(intent.now && intent.now > previousNow + ? { now: intent.now } + : {}), + } + : {}), }, }, }); @@ -263,12 +263,12 @@ export class CandleRepository { // Preserve coverage outside the requested reconciliation boundaries. for (const previous of old) { for (const part of [ - ...(previous.from < range.from ? - [{ from: previous.from, to: range.from }] : - []), - ...(previous.to > range.to ? - [{ from: range.to, to: previous.to }] : - []), + ...(previous.from < range.from + ? [{ from: previous.from, to: range.from }] + : []), + ...(previous.to > range.to + ? [{ from: range.to, to: previous.to }] + : []), ]) { await tx.tbankCandleCoverage.create({ data: { ...previous, ...part }, diff --git a/src/modules/investments/instruments/infrastructure/catalog-discovery.repository.ts b/src/modules/investments/instruments/infrastructure/catalog-discovery.repository.ts index e0c916a..46fe512 100644 --- a/src/modules/investments/instruments/infrastructure/catalog-discovery.repository.ts +++ b/src/modules/investments/instruments/infrastructure/catalog-discovery.repository.ts @@ -66,8 +66,8 @@ export class CatalogDiscoveryRepository { read(input: CatalogQuery) { return this.prisma.$transaction(async (tx) => { - const alias = input.exactTicker ? - await tx.tbankInstrumentLookup.findUnique({ + const alias = input.exactTicker + ? await tx.tbankInstrumentLookup.findUnique({ where: { provider_identifierType_identifier: { provider, @@ -75,17 +75,17 @@ export class CatalogDiscoveryRepository { identifier: input.query, }, }, - }) : - null; - const discovery = input.exactTicker ? - alias ? - { + }) + : null; + const discovery = input.exactTicker + ? alias + ? { instrumentUids: alias.matchedUids, fetchedAt: alias.fetchedAt, dataRevision: alias.dataRevision, - } : - null : - await tx.tbankCatalogDiscovery.findUnique({ + } + : null + : await tx.tbankCatalogDiscovery.findUnique({ where: { provider_instrumentType_query: { provider, @@ -134,9 +134,9 @@ export class CatalogDiscoveryRepository { kind: input.kind, source: sourceProvider(input), rangeKey: - input.kind === 'detail' ? - input.instrumentType : - (input.rangeKey ?? ''), + input.kind === 'detail' + ? input.instrumentType + : (input.rangeKey ?? ''), })), }, }); @@ -232,9 +232,9 @@ export class CatalogDiscoveryRepository { increment: isDeepStrictEqual( previousAlias?.matchedUids, matchedUids, - ) ? - UNCHANGED_REVISION_INCREMENT : - REVISION_INCREMENT, + ) + ? UNCHANGED_REVISION_INCREMENT + : REVISION_INCREMENT, }, }, }); @@ -246,9 +246,9 @@ export class CatalogDiscoveryRepository { source: sourceProvider(input), kind: input.kind, rangeKey: - input.kind === 'detail' ? - input.instrumentType : - (input.rangeKey ?? ''), + input.kind === 'detail' + ? input.instrumentType + : (input.rangeKey ?? ''), }; const previous = await tx.tbankInstrumentSource.findUnique({ where: { provider_instrumentUid_source_kind_rangeKey: key }, @@ -359,9 +359,9 @@ export class CatalogDiscoveryRepository { instrumentKind: instrument.instrumentKind, apiTradeAvailableFlag: instrument.apiTradeAvailableFlag, currency: instrument.currency, - brand: instrument.brand ? - { logoName: instrument.brand.logoName } : - undefined, + brand: instrument.brand + ? { logoName: instrument.brand.logoName } + : undefined, }), ) as Prisma.InputJsonObject; await tx.tbankInstrument.upsert({ @@ -418,9 +418,9 @@ export class CatalogDiscoveryRepository { increment: isDeepStrictEqual( previous?.matchedUids, instrumentUids, - ) ? - UNCHANGED_REVISION_INCREMENT : - REVISION_INCREMENT, + ) + ? UNCHANGED_REVISION_INCREMENT + : REVISION_INCREMENT, }, }, }); @@ -444,9 +444,9 @@ export class CatalogDiscoveryRepository { increment: isDeepStrictEqual( previous?.instrumentUids, instrumentUids, - ) ? - UNCHANGED_REVISION_INCREMENT : - REVISION_INCREMENT, + ) + ? UNCHANGED_REVISION_INCREMENT + : REVISION_INCREMENT, }, }, }); @@ -480,9 +480,9 @@ export class CatalogDiscoveryRepository { } function sourceProvider(input: CatalogSource) { - return input.kind === 'yield' || input.kind === 'bond-events' ? - 'moex' : - input.kind === 'rating' ? - 'ncr' : - 'tbank'; + return input.kind === 'yield' || input.kind === 'bond-events' + ? 'moex' + : input.kind === 'rating' + ? 'ncr' + : 'tbank'; } diff --git a/src/modules/investments/instruments/stocks/application/get-stock-company.use-case.ts b/src/modules/investments/instruments/stocks/application/get-stock-company.use-case.ts index ea4df48..fcc3560 100644 --- a/src/modules/investments/instruments/stocks/application/get-stock-company.use-case.ts +++ b/src/modules/investments/instruments/stocks/application/get-stock-company.use-case.ts @@ -23,8 +23,8 @@ export class GetStockCompanyUseCase { observe, ); return { - data: result.values.instrument ? - { + data: result.values.instrument + ? { description: result.values.description ?? null, metrics: metrics( result.values.fundamentals ?? null, @@ -32,8 +32,8 @@ export class GetStockCompanyUseCase { ), unavailable: result.unavailable, observedAt: result.observedAt, - } : - null, + } + : null, meta: result.meta, }; } @@ -51,22 +51,22 @@ export function metrics( ][] = [ ['marketCapitalization', 'money', null], ['peRatioTtm', 'ratio', 'TTM'], - ...(bank ? - ([ + ...(bank + ? ([ ['priceToBookTtm', 'ratio', 'TTM'], ['roe', 'percent', null], - ] as typeof selection) : - ([ + ] as typeof selection) + : ([ ['revenueTtm', 'money', 'TTM'], ['ebitdaTtm', 'money', 'TTM'], ] as typeof selection)), ['netIncomeTtm', 'money', 'TTM'], - ...(!bank ? - ([ + ...(!bank + ? ([ ['totalDebtMrq', 'money', 'MRQ'], ['netDebtToEbitda', 'ratio', null], - ] as typeof selection) : - []), + ] as typeof selection) + : []), ]; return selection.flatMap(([key, unit, period]) => { // eslint-disable-next-line security/detect-object-injection -- Keys come from the fixed metric selection declared above. @@ -74,8 +74,8 @@ export function metrics( const currency = values.currency?.toUpperCase() || null; return typeof value === 'number' && Number.isFinite(value) && - (unit !== 'money' || currency) ? - [ + (unit !== 'money' || currency) + ? [ { key, value, @@ -83,7 +83,7 @@ export function metrics( period, currency: unit === 'money' ? currency : null, }, - ] : - []; + ] + : []; }); } diff --git a/src/modules/investments/instruments/stocks/application/stock-catalog.service.ts b/src/modules/investments/instruments/stocks/application/stock-catalog.service.ts index 30fc5c9..bb3762a 100644 --- a/src/modules/investments/instruments/stocks/application/stock-catalog.service.ts +++ b/src/modules/investments/instruments/stocks/application/stock-catalog.service.ts @@ -34,9 +34,9 @@ export class StockCatalogService { data: result.data?.map((item) => { const short = stock(item); - const detail = item.detail ? - stock({ ...item.detail, uid: item.uid }) : - short; + const detail = item.detail + ? stock({ ...item.detail, uid: item.uid }) + : short; const last = item.price; return { ...detail, @@ -44,12 +44,12 @@ export class StockCatalogService { isin: detail.isin ?? short.isin, apiTradeAvailable: short.apiTradeAvailable, price: - last?.price && detail.currency ? - { + last?.price && detail.currency + ? { currency: detail.currency, value: formatDecimal(decimal(last.price)), - } : - null, + } + : null, priceAt: timestamp(last?.time), }; }) ?? null, @@ -64,9 +64,9 @@ export class StockCatalogService { const result = await this.discovery.detail(userId, 'share', uid, observe); return { ...result, - data: result.data ? - { ...stock(result.data), slug: result.data.slug } : - null, + data: result.data + ? { ...stock(result.data), slug: result.data.slug } + : null, }; } @@ -83,9 +83,9 @@ export class StockCatalogService { ); return { ...result, - data: result.data ? - { ...stock(result.data), slug: result.data.slug } : - null, + data: result.data + ? { ...stock(result.data), slug: result.data.slug } + : null, }; } } diff --git a/src/modules/investments/instruments/stocks/application/stock-income.service.ts b/src/modules/investments/instruments/stocks/application/stock-income.service.ts index dceabb4..cdb47c4 100644 --- a/src/modules/investments/instruments/stocks/application/stock-income.service.ts +++ b/src/modules/investments/instruments/stocks/application/stock-income.service.ts @@ -69,18 +69,18 @@ export class StockIncomeService { throw new NotFoundException(); } const events = sources[1].payload?.dividends; - const position = portfolio.data ? - instrumentPosition(portfolio.data, uid, 'STOCK') : - null; - const projected = events ? - stockDividends( + const position = portfolio.data + ? instrumentPosition(portfolio.data, uid, 'STOCK') + : null; + const projected = events + ? stockDividends( events, position, { from, to, today }, sources[1].meta.lastSuccessfulAt ?? new Date().toISOString(), !portfolio.data, - ) : - undefined; + ) + : undefined; const result = await this.forecasts.result({ userId, revision: connection.revision, @@ -91,9 +91,9 @@ export class StockIncomeService { candidate: portfolio.data && sources[0].instrument ? projected : undefined, }); - return result.data || !projected ? - result : - { + return result.data || !projected + ? result + : { data: stockDividends( events ?? [], null, @@ -117,9 +117,9 @@ export function stockDividends( const quantity = position && position.accounts.length && - position.accounts.every((p) => decimalFromString(p.quantity).value > ZERO_QUANTITY) ? - decimalFromString(position.quantity) : - null; + position.accounts.every((p) => decimalFromString(p.quantity).value > ZERO_QUANTITY) + ? decimalFromString(position.quantity) + : null; const dividends: StockDividendDto[] = events .filter((event) => event.dividendType?.toLowerCase() !== 'cancelled') .map((event) => { @@ -128,29 +128,29 @@ export function stockDividends( const lastBuyDate = timestamp(event.lastBuyDate)?.slice(ISO_DATE_START, ISO_DATE_LENGTH) ?? null; const currency = event.dividendNet?.currency?.toUpperCase(); const perShare = - currency && event.dividendNet ? - { currency, value: formatDecimal(decimal(event.dividendNet)) } : - null; + currency && event.dividendNet + ? { currency, value: formatDecimal(decimal(event.dividendNet)) } + : null; // A future payment can have an already-fixed register: keep the event, but do not estimate entitlement from today's holding. const eventDate = paymentDate ?? recordDate; - const timing: StockDividendDto['timing'] = eventDate ? - eventDate >= today ? - 'upcoming' : - 'past' : - 'undated'; + const timing: StockDividendDto['timing'] = eventDate + ? eventDate >= today + ? 'upcoming' + : 'past' + : 'undated'; const estimatedAmount = quantity && perShare && recordDate && recordDate >= today && - timing === 'upcoming' ? - { + timing === 'upcoming' + ? { currency: perShare.currency, value: formatDecimal( multiply(quantity, decimalFromString(perShare.value)), ), - } : - null; + } + : null; return { perShare, recordDate, diff --git a/src/modules/investments/instruments/stocks/application/stock-market.service.ts b/src/modules/investments/instruments/stocks/application/stock-market.service.ts index c4b3912..e03ad7a 100644 --- a/src/modules/investments/instruments/stocks/application/stock-market.service.ts +++ b/src/modules/investments/instruments/stocks/application/stock-market.service.ts @@ -44,9 +44,9 @@ export class StockMarketService { observe, ); return { - data: result.values.instrument ? - stockQuote(result.values, result.unavailable, result.observedAt) : - null, + data: result.values.instrument + ? stockQuote(result.values, result.unavailable, result.observedAt) + : null, meta: result.meta, }; } @@ -75,20 +75,20 @@ export function stockQuote( const priceAt = timestamp(last?.time); const currency = share?.currency?.toUpperCase(); const price = - last?.price && currency ? - { currency, value: formatDecimal(decimal(last.price)) } : - null; + last?.price && currency + ? { currency, value: formatDecimal(decimal(last.price)) } + : null; const previousPrice = - previous?.close && currency ? - { currency, value: formatDecimal(decimal(previous.close)) } : - null; + previous?.close && currency + ? { currency, value: formatDecimal(decimal(previous.close)) } + : null; const difference = - price && previousPrice ? - subtract( + price && previousPrice + ? subtract( decimalFromString(price.value), decimalFromString(previousPrice.value), - ) : - null; + ) + : null; return { price, priceAt, @@ -96,21 +96,21 @@ export function stockQuote( previousCloseDate: timestamp(previous?.time)?.slice(ISO_DATE_START, ISO_DATE_LENGTH) ?? null, previousClose: previousPrice, change: - difference && currency ? - { currency, value: formatDecimal(difference) } : - null, + difference && currency + ? { currency, value: formatDecimal(difference) } + : null, changePercent: difference && previousPrice && - decimalFromString(previousPrice.value).value > ZERO_AMOUNT ? - formatDecimal( + decimalFromString(previousPrice.value).value > ZERO_AMOUNT + ? formatDecimal( divide( multiply(difference, decimalFromString('100')), decimalFromString(previousPrice.value), CHANGE_PERCENT_DECIMAL_PLACES, ), - ) : - null, + ) + : null, unavailable, observedAt, }; diff --git a/src/modules/investments/investment-resource.mapper.ts b/src/modules/investments/investment-resource.mapper.ts index dc7da49..e072acf 100644 --- a/src/modules/investments/investment-resource.mapper.ts +++ b/src/modules/investments/investment-resource.mapper.ts @@ -16,11 +16,11 @@ export function investmentResourceMeta(input: { const ownSync = { id: input.work?.id ?? null, state: - input.work?.state === 'running' ? - ('running' as const) : - input.work?.state === 'failed' ? - ('failed' as const) : - ('idle' as const), + input.work?.state === 'running' + ? ('running' as const) + : input.work?.state === 'failed' + ? ('failed' as const) + : ('idle' as const), errorCode: input.work?.errorCode ?? null, }; const states = [ @@ -34,11 +34,11 @@ export function investmentResourceMeta(input: { states.find((state) => state.state === 'queued') ?? states.find((state) => state.state === 'failed') ?? ownSync; - const sourceFreshness = input.fetchedAt ? - Date.now() - input.fetchedAt.valueOf() < input.freshForMs ? - ('fresh' as const) : - ('stale' as const) : - ('unknown' as const); + const sourceFreshness = input.fetchedAt + ? Date.now() - input.fetchedAt.valueOf() < input.freshForMs + ? ('fresh' as const) + : ('stale' as const) + : ('unknown' as const); const freshness = [ sourceFreshness, ...(input.dependencies ?? []) @@ -56,16 +56,16 @@ export function investmentResourceMeta(input: { .sort(); const meta = { resourceKey: input.resourceKey, - ...(input.unavailableReason ? - { unavailableReason: input.unavailableReason } : - {}), + ...(input.unavailableReason + ? { unavailableReason: input.unavailableReason } + : {}), dataRevision: input.dataRevision, availability: input.availability, - freshness: freshness.includes('unknown') ? - ('unknown' as const) : - freshness.includes('stale') ? - ('stale' as const) : - ('fresh' as const), + freshness: freshness.includes('unknown') + ? ('unknown' as const) + : freshness.includes('stale') + ? ('stale' as const) + : ('fresh' as const), lastSuccessfulAt: timestamps[0] ?? null, sync, ...(input.coverage ? { coverage: input.coverage } : {}), @@ -79,9 +79,9 @@ export function investmentResourceMeta(input: { ...meta, // The requested window is identified by resourceKey. Its moving "now" // boundary is not a change to saved coverage between status and GET. - coverage: meta.coverage ? - { ...meta.coverage, requested: undefined } : - undefined, + coverage: meta.coverage + ? { ...meta.coverage, requested: undefined } + : undefined, }), ) .digest('base64url'), diff --git a/src/modules/investments/investment-sync-plan.ts b/src/modules/investments/investment-sync-plan.ts index 50de3d0..c907c1c 100644 --- a/src/modules/investments/investment-sync-plan.ts +++ b/src/modules/investments/investment-sync-plan.ts @@ -84,16 +84,16 @@ export function planInvestmentScreen( const unavailable: { reason: string; resourceKey: string }[] = []; const operations = (from: Date, to: Date, instrumentUid?: string) => { const targets = accounts.flatMap((account) => - from < to ? - [ + from < to + ? [ { accountId: account.accountId, from, to, ...(instrumentUid ? { instrumentUid } : {}), }, - ] : - [], + ] + : [], ); sources.push( ...operationIntervals(targets).map((range) => ({ @@ -112,12 +112,12 @@ export function planInvestmentScreen( })), ); const knownOpening = - accounts.length && accounts.every((account) => account.openedOn) ? - new Date( + accounts.length && accounts.every((account) => account.openedOn) + ? new Date( // eslint-disable-next-line sonarjs/no-alphabetical-sort -- Code-unit ordering is intentional for canonical keys and ISO dates; locale collation would change persisted bindings. `${accounts.map((account) => account.openedOn ?? '').sort()[0]}T00:00:00+03:00`, - ) : - undefined; + ) + : undefined; if (!context.membershipKnown) return { sources, unavailable, now }; if ( ['account', 'accounts', 'bond', 'events', 'stock'].includes(screen.screen) @@ -146,9 +146,9 @@ export function planInvestmentScreen( ); for (const range of [ period.selected, - ...(screen.screen === 'income' ? - [period.previous, ...(period.yearAgo ? [period.yearAgo] : [])] : - []), + ...(screen.screen === 'income' + ? [period.previous, ...(period.yearAgo ? [period.yearAgo] : [])] + : []), ]) { const bounds = dateOnlyBounds(range, now); operations(bounds.from, bounds.to); @@ -216,11 +216,11 @@ export function planInvestmentScreen( } const range = screen.candleRange ?? - (period === 'all' ? - instrument.historyFrom ? - { from: instrument.historyFrom, to: now } : - null : - { from, to: now }); + (period === 'all' + ? instrument.historyFrom + ? { from: instrument.historyFrom, to: now } + : null + : { from, to: now }); if (range) { sources.push({ ...identity, @@ -237,17 +237,17 @@ export function planInvestmentScreen( } if (['account', 'bond', 'events', 'stock'].includes(screen.screen)) { const day = - screen.screen === 'stock' || screen.screen === 'bond' ? - moscowDate(now) : - now.toISOString().slice(ISO_DATE_START, ISO_DATE_LENGTH); + screen.screen === 'stock' || screen.screen === 'bond' + ? moscowDate(now) + : now.toISOString().slice(ISO_DATE_START, ISO_DATE_LENGTH); const from = - screen.screen === 'events' && screen.from ? - new Date(`${screen.from}T00:00:00Z`) : - new Date(`${day}T00:00:00Z`); + screen.screen === 'events' && screen.from + ? new Date(`${screen.from}T00:00:00Z`) + : new Date(`${day}T00:00:00Z`); const to = - screen.screen === 'events' && screen.to ? - new Date(`${screen.to}T00:00:00Z`) : - new Date(from); + screen.screen === 'events' && screen.to + ? new Date(`${screen.to}T00:00:00Z`) + : new Date(from); if ( !(screen.screen === 'events' && screen.to) && screen.screen !== 'stock' && @@ -309,9 +309,9 @@ export function accountHistory( ): OperationInterval[] { return accounts.flatMap((account) => { const from = new Date( - account.openedOn ? - `${account.openedOn}T00:00:00Z` : - '1970-01-01T00:00:00Z', + account.openedOn + ? `${account.openedOn}T00:00:00Z` + : '1970-01-01T00:00:00Z', ); return from < now ? [{ accountId: account.accountId, from, to: now }] : []; }); @@ -324,22 +324,22 @@ export function ledgerRange( accounts: Account[], now: Date, ) { - const opening = accounts.every((account) => account.openedOn) ? + const opening = accounts.every((account) => account.openedOn) // eslint-disable-next-line sonarjs/no-alphabetical-sort -- Code-unit ordering is intentional for canonical keys and ISO dates; locale collation would change persisted bindings. - accounts.map((account) => account.openedOn ?? '').sort()[0] : - undefined; - const from = fromValue ? - new Date(fromValue) : - instrumentUid ? - new Date(opening ? `${opening}T00:00:00Z` : '1970-01-01T00:00:00Z') : - new Date(Date.UTC(now.getUTCFullYear(), JANUARY_INDEX, FIRST_DAY_OF_MONTH)); - const end = toValue ? - new Date( - /^\d{4}-\d{2}-\d{2}$/.test(toValue) ? - `${toValue}T23:59:59.999Z` : - toValue, - ) : - now; + ? accounts.map((account) => account.openedOn ?? '').sort()[0] + : undefined; + const from = fromValue + ? new Date(fromValue) + : instrumentUid + ? new Date(opening ? `${opening}T00:00:00Z` : '1970-01-01T00:00:00Z') + : new Date(Date.UTC(now.getUTCFullYear(), JANUARY_INDEX, FIRST_DAY_OF_MONTH)); + const end = toValue + ? new Date( + /^\d{4}-\d{2}-\d{2}$/.test(toValue) + ? `${toValue}T23:59:59.999Z` + : toValue, + ) + : now; return { from, to: new Date(Math.min(end.valueOf() + (toValue ? EXCLUSIVE_END_OFFSET_MS : NO_END_OFFSET_MS), now.valueOf())), diff --git a/src/modules/investments/operations/application/brokerage-operations-sync.service.ts b/src/modules/investments/operations/application/brokerage-operations-sync.service.ts index 0bf9456..fdaad98 100644 --- a/src/modules/investments/operations/application/brokerage-operations-sync.service.ts +++ b/src/modules/investments/operations/application/brokerage-operations-sync.service.ts @@ -82,24 +82,24 @@ export class BrokerageOperationsSyncService { ...intervals.map((interval) => interval.to.valueOf()), ); const targets = intervals.map((interval) => - options.movingEnd && interval.to.valueOf() === movingTo ? - { ...interval, to: now } : - interval, + options.movingEnd && interval.to.valueOf() === movingTo + ? { ...interval, to: now } + : interval, ); const plan = planOperationRead(targets, coverage, now, options); const complete = plan.intervals.every( (interval) => interval.availability === 'complete', ); const handle = - !options.observe && plan.load.length ? - await this.request( + !options.observe && plan.load.length + ? await this.request( userId, plan.load, complete || Boolean(options.force), 'screen', connection.revision, - ) : - null; + ) + : null; if (!options.observe && !options.force && options.history) { const accountIds = new Set( intervals.map((interval) => interval.accountId), @@ -267,9 +267,9 @@ export class BrokerageOperationsSyncService { Math.ceil(interval.to.valueOf() / MILLISECONDS_PER_SECOND) + UPSTREAM_RANGE_MARGIN_SECONDS, ), }, - ...(interval.instrumentUid ? - { instrumentId: interval.instrumentUid } : - {}), + ...(interval.instrumentUid + ? { instrumentId: interval.instrumentUid } + : {}), cursor, limit: operationsPageSize, state: 'OPERATION_STATE_EXECUTED', diff --git a/src/modules/investments/operations/application/brokerage-operations.service.ts b/src/modules/investments/operations/application/brokerage-operations.service.ts index eb6b802..3a43f43 100644 --- a/src/modules/investments/operations/application/brokerage-operations.service.ts +++ b/src/modules/investments/operations/application/brokerage-operations.service.ts @@ -86,13 +86,13 @@ export class BrokerageOperationsService { const filterKey = JSON.stringify([ accountId, from.toISOString(), - query.to ? - new Date( - /^\d{4}-\d{2}-\d{2}$/.test(query.to) ? - `${query.to}T23:59:59.999Z` : - query.to, - ).toISOString() : - 'now', + query.to + ? new Date( + /^\d{4}-\d{2}-\d{2}$/.test(query.to) + ? `${query.to}T23:59:59.999Z` + : query.to, + ).toISOString() + : 'now', query.instrumentUid ?? null, categories, limit, @@ -129,9 +129,9 @@ export class BrokerageOperationsService { source.priority === 'screen' && source.accountId && source.from && - source.to ? - [{ accountId: source.accountId, from: source.from, to: source.to }] : - [], + source.to + ? [{ accountId: source.accountId, from: source.from, to: source.to }] + : [], ); if (query.sync !== 'observe') { const discovery = await this.membership.request( @@ -159,9 +159,9 @@ export class BrokerageOperationsService { accountId === 'all' || account.accountId === accountId, ) .flatMap((account) => - currentRange.from < currentRange.to ? - [{ accountId: account.accountId, ...currentRange }] : - [], + currentRange.from < currentRange.to + ? [{ accountId: account.accountId, ...currentRange }] + : [], ); await this.sync.readPlan(userId, targets, { movingEnd, @@ -181,16 +181,16 @@ export class BrokerageOperationsService { const saved = await this.repository.readPage(readInput); const accounts = saved?.connection.accounts ?? []; const targets = accounts.flatMap((account) => - from < to ? - [ + from < to + ? [ { accountId: account.accountId, from, to, instrumentUid: query.instrumentUid, }, - ] : - [], + ] + : [], ); const plan = planOperationRead(targets, saved?.coverage ?? [], now, { movingEnd, @@ -296,17 +296,17 @@ export class BrokerageOperationsService { const meta = investmentResourceMeta({ resourceKey: `operations/${createHash('sha256').update(filterKey).digest('base64url')}`, dataRevision: saved?.binding ?? 'missing', - availability: !available ? - 'missing' : - complete ? - 'complete' : - confirmed.length ? - 'partial' : - 'missing', + availability: !available + ? 'missing' + : complete + ? 'complete' + : confirmed.length + ? 'partial' + : 'missing', fetchedAt: lastSuccessfulAt, - freshForMs: plan.intervals.some((interval) => interval.stale) ? - STALE_FRESHNESS_MS : - Number.POSITIVE_INFINITY, + freshForMs: plan.intervals.some((interval) => interval.stale) + ? STALE_FRESHNESS_MS + : Number.POSITIVE_INFINITY, work: work ?? saved?.membershipWork ?? null, dependencies: [membershipMeta, ...metadataDependencies], coverage: { @@ -320,8 +320,8 @@ export class BrokerageOperationsService { (interval) => interval.accountId === account.accountId && interval.asOf, ), - ) ? - new Date( + ) + ? new Date( Math.min( ...accounts.map((account) => Math.max( @@ -334,42 +334,42 @@ export class BrokerageOperationsService { ), ), ), - ).toISOString() : - null, + ).toISOString() + : null, historyBoundary: knownBoundary ? 'known' : 'unknown', historyState: saved?.work.some( (work) => work.state === 'running' && work.priority > operationsSyncPriority.screen, - ) ? - 'loading' : - saved?.work.some( + ) + ? 'loading' + : saved?.work.some( (work) => work.state === 'failed' && work.priority > operationsSyncPriority.screen, - ) ? - 'failed' : - knownBoundary && - history.intervals.every( - (interval) => interval.availability === 'complete', - ) ? - 'complete' : - saved?.coverage.length ? - 'partial' : - 'missing', + ) + ? 'failed' + : knownBoundary && + history.intervals.every( + (interval) => interval.availability === 'complete', + ) + ? 'complete' + : saved?.coverage.length + ? 'partial' + : 'missing', }, }); const last = rows.at(LAST_ROW_INDEX); const hasNext = Boolean(saved && saved.rows.length > limit); return { data: - meta.availability === 'missing' ? - null : - { + meta.availability === 'missing' + ? null + : { accountId, items: rows.map((row) => ({ ...mapBrokerageOperation( row.payload as TbankOperationItem, row.occurredAt, ), - ...(accountId === 'all' ? - { + ...(accountId === 'all' + ? { account: { id: row.accountId, name: @@ -377,8 +377,8 @@ export class BrokerageOperationsService { (account) => account.accountId === row.accountId, )?.name ?? row.accountId, }, - } : - {}), + } + : {}), logoName: instruments .get(metadataKey(row.payload as TbankOperationItem)) @@ -386,16 +386,16 @@ export class BrokerageOperationsService { })), hasNext, nextCursor: - hasNext && last && saved ? - this.encodeCursor({ + hasNext && last && saved + ? this.encodeCursor({ binding: saved.binding, movingEnd, to: to.toISOString(), at: last.occurredAt.toISOString(), accountId: last.accountId, id: last.brokerOperationId, - }) : - null, + }) + : null, observedAt: now.toISOString(), }, meta, @@ -560,13 +560,13 @@ export class BrokerageOperationsService { // eslint-disable-next-line security/detect-object-injection -- The numeric index enumerates the three comparison periods. resourceKey: `income/${accountId}/${['selected', 'previous', 'yearAgo'][index]}/${period.key}`, dataRevision: saved?.binding ?? 'missing', - availability: !known ? - 'missing' : - complete ? - 'complete' : - sources.length ? - 'partial' : - 'missing', + availability: !known + ? 'missing' + : complete + ? 'complete' + : sources.length + ? 'partial' + : 'missing', fetchedAt, freshForMs: plan.intervals.some((range) => range.stale) ? STALE_FRESHNESS_MS : Infinity, work, @@ -579,8 +579,8 @@ export class BrokerageOperationsService { confirmed, ).map(serialize), asOf: - complete && targets.length ? - new Date( + complete && targets.length + ? new Date( Math.min( ...targets.map((target) => Math.max( @@ -595,24 +595,24 @@ export class BrokerageOperationsService { ), ), ), - ).toISOString() : - null, + ).toISOString() + : null, historyBoundary: (saved?.connection.accounts ?? []).every( (account) => account.openedOn, - ) ? - 'known' : - 'unknown', - historyState: complete ? - 'complete' : - sources.length ? - 'partial' : - 'missing', + ) + ? 'known' + : 'unknown', + historyState: complete + ? 'complete' + : sources.length + ? 'partial' + : 'missing', }, }); return { meta, - items: complete ? - (saved?.rows ?? []) + items: complete + ? (saved?.rows ?? []) .filter((row) => plan.intervals.some( (interval) => @@ -626,8 +626,8 @@ export class BrokerageOperationsService { row.payload as TbankOperationItem, row.occurredAt, ), - ...(accountId === 'all' ? - { + ...(accountId === 'all' + ? { account: { id: row.accountId, name: @@ -635,10 +635,10 @@ export class BrokerageOperationsService { (account) => account.accountId === row.accountId, )?.name ?? row.accountId, }, - } : - {}), - })) : - null, + } + : {}), + })) + : null, }; }); } diff --git a/src/modules/investments/operations/domain/operations-sync-plan.ts b/src/modules/investments/operations/domain/operations-sync-plan.ts index 3105e64..6477330 100644 --- a/src/modules/investments/operations/domain/operations-sync-plan.ts +++ b/src/modules/investments/operations/domain/operations-sync-plan.ts @@ -93,12 +93,12 @@ export function uncoveredOperationIntervals( return [fragment]; } return [ - ...(fragment.from < covered.from ? - [{ ...fragment, to: covered.from }] : - []), - ...(covered.to < fragment.to ? - [{ ...fragment, from: covered.to }] : - []), + ...(fragment.from < covered.from + ? [{ ...fragment, to: covered.from }] + : []), + ...(covered.to < fragment.to + ? [{ ...fragment, from: covered.to }] + : []), ]; }); } @@ -176,20 +176,20 @@ export function planOperationRead( // Completeness is bounded by the observed end, never by the new wall clock. return { ...effective, - availability: complete ? - ('complete' as const) : - sources.length ? - ('partial' as const) : - ('missing' as const), + availability: complete + ? ('complete' as const) + : sources.length + ? ('partial' as const) + : ('missing' as const), stale, - asOf: sources.length ? - new Date( + asOf: sources.length + ? new Date( Math.min( effective.to.valueOf(), Math.max(...sources.map((row) => row.asOf.valueOf())), ), - ) : - null, + ) + : null, }; }); return { intervals, load: operationIntervals(load) }; diff --git a/src/modules/investments/operations/infrastructure/brokerage-operation.mapper.ts b/src/modules/investments/operations/infrastructure/brokerage-operation.mapper.ts index 735c04b..17782cb 100644 --- a/src/modules/investments/operations/infrastructure/brokerage-operation.mapper.ts +++ b/src/modules/investments/operations/infrastructure/brokerage-operation.mapper.ts @@ -61,14 +61,14 @@ export function mapBrokerageOperation( logoName: null, payment: money(operation.payment), quantity: - (operationCategory(operation.type) === 'trade' ? - operation.quantityDone : - operation.quantity) === undefined ? - null : - String( - operationCategory(operation.type) === 'trade' ? - operation.quantityDone : - operation.quantity, + (operationCategory(operation.type) === 'trade' + ? operation.quantityDone + : operation.quantity) === undefined + ? null + : String( + operationCategory(operation.type) === 'trade' + ? operation.quantityDone + : operation.quantity, ), price: money(operation.price), accruedInterest: money(operation.accruedInt), @@ -82,9 +82,9 @@ function text(value: string | undefined): null | string { function money(value: TbankMoney | undefined) { const currency = value?.currency?.trim().toUpperCase(); - return currency && value ? - { currency, value: formatDecimal(decimal(value)) } : - null; + return currency && value + ? { currency, value: formatDecimal(decimal(value)) } + : null; } export function operationTimestamp( diff --git a/src/modules/investments/operations/infrastructure/brokerage-operations.repository.ts b/src/modules/investments/operations/infrastructure/brokerage-operations.repository.ts index 2d3adc9..5c08d06 100644 --- a/src/modules/investments/operations/infrastructure/brokerage-operations.repository.ts +++ b/src/modules/investments/operations/infrastructure/brokerage-operations.repository.ts @@ -110,9 +110,9 @@ export class BrokerageOperationsRepository { // eslint-disable-next-line sonarjs/no-alphabetical-sort -- Code-unit ordering is intentional for canonical keys and ISO dates; locale collation would change persisted bindings. const oldest = accounts.map((account) => account.openedOn).sort()[0]; const openedAt = - oldest && accounts.every((account) => account.openedOn) ? - new Date(`${oldest}T00:00:00+03:00`) : - undefined; + oldest && accounts.every((account) => account.openedOn) + ? new Date(`${oldest}T00:00:00+03:00`) + : undefined; const periods = input.periods?.(openedAt).map((period) => { const now = new Date(); const bounds = dateOnlyBounds(period, now); @@ -176,21 +176,21 @@ export class BrokerageOperationsRepository { const rows = await tx.tbankOperation.findMany({ where: { ...scope, - ...(periods ? - { + ...(periods + ? { OR: periods.map((period) => ({ occurredAt: { gte: period.from, lt: period.to }, })), - } : - { occurredAt: { gte: input.from, lt: input.to } }), - ...(input.instrumentUid ? - { instrumentUid: input.instrumentUid } : - {}), - ...(input.categories.length ? - { category: { in: input.categories } } : - {}), - ...(after ? - { + } + : { occurredAt: { gte: input.from, lt: input.to } }), + ...(input.instrumentUid + ? { instrumentUid: input.instrumentUid } + : {}), + ...(input.categories.length + ? { category: { in: input.categories } } + : {}), + ...(after + ? { OR: [ { occurredAt: { lt: new Date(after.at) } }, { @@ -203,8 +203,8 @@ export class BrokerageOperationsRepository { brokerOperationId: { gt: after.id }, }, ], - } : - {}), + } + : {}), }, orderBy: [ { occurredAt: 'desc' }, @@ -305,9 +305,9 @@ export class BrokerageOperationsRepository { }, data: { historyFrom: new Date( - account.openedOn ? - `${account.openedOn}T00:00:00Z` : - '1970-01-01T00:00:00Z', + account.openedOn + ? `${account.openedOn}T00:00:00Z` + : '1970-01-01T00:00:00Z', ), historyBoundary: account.openedOn ? 'known' : 'unknown', }, @@ -383,42 +383,42 @@ export class BrokerageOperationsRepository { ) { const pending = previousIntent?.intervals.map(decodeInterval) ?? []; const additions = - previous.scopeKey === scopeKey ? - [] : - uncoveredOperationIntervals(intervals, pending); + previous.scopeKey === scopeKey + ? [] + : uncoveredOperationIntervals(intervals, pending); const work = await tx.tbankSyncWork.update({ where: { id: previous.id }, data: { intent: { atomic: previousIntent?.atomic ?? publicationAtomic, - atomicIntervals: previousIntent?.atomic ? - [ + atomicIntervals: previousIntent?.atomic + ? [ ...new Set([ ...(previousIntent.atomicIntervals ?? previousIntent.intervals), ...additions.map(intervalKey), ]), - ] : - [], + ] + : [], screenIntervals: [ ...new Set([ ...(previousIntent?.screenIntervals ?? - (previous.priority === operationsSyncPriority.screen ? - (previousIntent?.intervals ?? []) : - [])), - ...(priority === operationsSyncPriority.screen ? - [...pending, ...additions] + (previous.priority === operationsSyncPriority.screen + ? (previousIntent?.intervals ?? []) + : [])), + ...(priority === operationsSyncPriority.screen + ? [...pending, ...additions] .filter((item) => intervals.some((target) => operationIntervalsOverlap(item, target), ), ) - .map(intervalKey) : - []), + .map(intervalKey) + : []), ]), ], - intervals: (priority < previous.priority ? - [ + intervals: (priority < previous.priority + ? [ ...pending.filter((item) => intervals.some((target) => operationIntervalsOverlap(item, target), @@ -431,8 +431,8 @@ export class BrokerageOperationsRepository { operationIntervalsOverlap(item, target), ), ), - ] : - [...pending, ...additions] + ] + : [...pending, ...additions] ).map(intervalKey), }, priority: Math.min(priority, previous.priority), @@ -444,9 +444,9 @@ export class BrokerageOperationsRepository { previous && previous.state !== 'complete' && previous.state !== 'superseded'; - const retained = recovering ? - (previousIntent?.intervals.map(decodeInterval) ?? []) : - []; + const retained = recovering + ? (previousIntent?.intervals.map(decodeInterval) ?? []) + : []; // A bridge between two active groups must fence both producers before // either can publish a subset of the newly combined result. for (const joined of touching) { @@ -469,23 +469,23 @@ export class BrokerageOperationsRepository { await tx.tbankSyncStage.deleteMany({ where: { workId: previous.id } }); } const targets = - recovering && !upgradeGroup && touching.length <= SINGLE_WORK_GROUP ? - previous?.scopeKey === scopeKey ? - retained : - [...retained, ...uncoveredOperationIntervals(intervals, retained)] : - operationIntervals([...retained, ...intervals]); + recovering && !upgradeGroup && touching.length <= SINGLE_WORK_GROUP + ? previous?.scopeKey === scopeKey + ? retained + : [...retained, ...uncoveredOperationIntervals(intervals, retained)] + : operationIntervals([...retained, ...intervals]); const atomicTargets = [ ...(publicationAtomic ? intervals : []), - ...(recovering && previousIntent?.atomic ? - (previousIntent.atomicIntervals ?? previousIntent.intervals).map( + ...(recovering && previousIntent?.atomic + ? (previousIntent.atomicIntervals ?? previousIntent.intervals).map( decodeInterval, - ) : - []), + ) + : []), ...touching.flatMap((work) => { const joined = work.intent as OperationsIntent; - return joined.atomic ? - (joined.atomicIntervals ?? joined.intervals).map(decodeInterval) : - []; + return joined.atomic + ? (joined.atomicIntervals ?? joined.intervals).map(decodeInterval) + : []; }), ]; const intent = { @@ -506,9 +506,9 @@ export class BrokerageOperationsRepository { )) || ( previousIntent?.screenIntervals ?? - (previous?.priority === operationsSyncPriority.screen ? - (previousIntent?.intervals ?? []) : - []) + (previous?.priority === operationsSyncPriority.screen + ? (previousIntent?.intervals ?? []) + : []) ).some((key) => operationIntervalsOverlap(target, decodeInterval(key)), ), @@ -563,20 +563,20 @@ export class BrokerageOperationsRepository { marker.payload as PageProgress, ]), ); - const group = intent.atomic ? - (intent.atomicIntervals ?? intent.intervals) : - intent.intervals; + const group = intent.atomic + ? (intent.atomicIntervals ?? intent.intervals) + : intent.intervals; const nextKey = group.find((key) => !progress.get(key)?.complete); return { priority: work.priority, atomic: intent.atomic, intervals: group.map(decodeInterval), - next: nextKey ? - { + next: nextKey + ? { interval: decodeInterval(nextKey), cursor: progress.get(nextKey)?.cursor || undefined, - } : - null, + } + : null, }; } @@ -795,9 +795,9 @@ export class BrokerageOperationsRepository { where: { ...scope, occurredAt: { gte: interval.from, lt: interval.to }, - ...(interval.instrumentUid ? - { instrumentUid: interval.instrumentUid } : - {}), + ...(interval.instrumentUid + ? { instrumentUid: interval.instrumentUid } + : {}), }, }); const incoming = new Set(rows.map((row) => row.itemKey)); @@ -902,9 +902,9 @@ export class BrokerageOperationsRepository { intent.screenIntervals ?? (work.priority === operationsSyncPriority.screen ? intent.intervals : []) ).includes(key), - ) ? - operationsSyncPriority.screen : - operationsSyncPriority.history, + ) + ? operationsSyncPriority.screen + : operationsSyncPriority.history, progress: { remainingIntervals: remaining.length }, }, }); diff --git a/src/modules/investments/portfolio/application/forecast.service.ts b/src/modules/investments/portfolio/application/forecast.service.ts index 13370d2..f896501 100644 --- a/src/modules/investments/portfolio/application/forecast.service.ts +++ b/src/modules/investments/portfolio/application/forecast.service.ts @@ -36,8 +36,8 @@ export class ForecastService { ...input, portfolioProjection: input.portfolio.dataRevision, portfolios: (input.portfolio.dependencies ?? []).flatMap((dependency) => - dependency.resourceKey.startsWith('portfolio/') ? - [ + dependency.resourceKey.startsWith('portfolio/') + ? [ { accountId: decodeURIComponent( dependency.resourceKey.slice('portfolio/'.length), @@ -46,21 +46,21 @@ export class ForecastService { (dependency as InvestmentResourceMetaDto).dataRevision, ), }, - ] : - [], + ] + : [], ), sources: input.sources.flatMap((source) => source.version ? [source.version] : [], ), timestamps: dependencies.flatMap((dependency) => - dependency.lastSuccessfulAt ? - [ + dependency.lastSuccessfulAt + ? [ { resourceKey: dependency.resourceKey, at: dependency.lastSuccessfulAt, }, - ] : - [], + ] + : [], ), }); const timestamps = stored?.sourceTimestamps as @@ -78,33 +78,33 @@ export class ForecastService { payload && 'positionIsStale' in payload && 'positionAt' in payload && - typeof payload.positionAt === 'string' ? - { + typeof payload.positionAt === 'string' + ? { ...payload, positionIsStale: tbankSourceNeedsRefresh( 'portfolio', new Date(payload.positionAt), new Date(Date.now()), ), - } : - payload; + } + : payload; return { data, meta: investmentResourceMeta({ resourceKey: input.scopeKey, dataRevision: String(stored?.dataRevision ?? MISSING_DATA_REVISION), - availability: stored ? - 'complete' : - dependencies.some( + availability: stored + ? 'complete' + : dependencies.some( (dependency) => dependency.availability !== 'missing', - ) ? - 'partial' : - 'missing', + ) + ? 'partial' + : 'missing', fetchedAt: first ? new Date(first) : null, freshForMs: - data && 'positionIsStale' in data && data.positionIsStale === true ? - STALE_FRESHNESS_MS : - Number.POSITIVE_INFINITY, + data && 'positionIsStale' in data && data.positionIsStale === true + ? STALE_FRESHNESS_MS + : Number.POSITIVE_INFINITY, work: work?.id ? { ...work, id: work.id } : null, dependencies, }), diff --git a/src/modules/investments/portfolio/application/portfolio.service.ts b/src/modules/investments/portfolio/application/portfolio.service.ts index dfd9390..e6930dc 100644 --- a/src/modules/investments/portfolio/application/portfolio.service.ts +++ b/src/modules/investments/portfolio/application/portfolio.service.ts @@ -186,15 +186,15 @@ export class PortfolioService { name: account.name, type: account.type, openedOn: account.openedOn, - snapshot: account.portfolio ? - { + snapshot: account.portfolio + ? { ...(account.portfolio.payload as unknown as PortfolioDto), lastSuccessfulAt: account.portfolio.fetchedAt.toISOString(), isStale: Date.now() - account.portfolio.fetchedAt.valueOf() >= portfolioFreshForMs, - } : - null, + } + : null, meta: investmentResourceMeta({ resourceKey: `portfolio/${encodeURIComponent(account.accountId)}`, dataRevision: String(account.portfolio?.dataRevision ?? MISSING_DATA_REVISION), @@ -220,8 +220,8 @@ export class PortfolioService { ); const metadataDependencies = [...metadata.entries()].flatMap( ([key, source]) => - source.complete && source.data ? - [ + source.complete && source.data + ? [ investmentResourceMeta({ resourceKey: `instrument/metadata/${key}`, dataRevision: source.dataRevision, @@ -230,20 +230,20 @@ export class PortfolioService { freshForMs: tbankSourceTtlMs.metadata, work: null, }), - ] : - [], + ] + : [], ); const portfolios = saved.accounts.flatMap((account) => - account.snapshot ? - [ + account.snapshot + ? [ { ...account.snapshot, positions: account.snapshot.positions.map((position) => { const shared = metadata.get(metadataKey(position))?.data; return { ...position, - ...(shared ? - { + ...(shared + ? { displayName: shared.name?.trim() || shared.ticker?.trim() || @@ -252,29 +252,29 @@ export class PortfolioService { isin: shared.isin?.trim() || position.isin, logoName: shared.brand?.logoName?.trim() || position.logoName, - } : - {}), - ...(accountId === 'all' ? - { account: { id: account.id, name: account.name } } : - {}), + } + : {}), + ...(accountId === 'all' + ? { account: { id: account.id, name: account.name } } + : {}), }; }), }, - ] : - [], + ] + : [], ); const lastSuccessfulAt = // eslint-disable-next-line sonarjs/no-alphabetical-sort -- Code-unit ordering is intentional for canonical keys and ISO dates; locale collation would change persisted bindings. portfolios.map((portfolio) => portfolio.lastSuccessfulAt).sort()[0] ?? saved.membership.lastSuccessfulAt; const data = - missing || !lastSuccessfulAt ? - null : - { + missing || !lastSuccessfulAt + ? null + : { lastSuccessfulAt, - isStale: portfolios.length ? - portfolios.some((portfolio) => portfolio.isStale) : - saved.membership.freshness !== 'fresh', + isStale: portfolios.length + ? portfolios.some((portfolio) => portfolio.isStale) + : saved.membership.freshness !== 'fresh', totalsByCurrency: sumMoneyByCurrency( portfolios.flatMap((portfolio) => portfolio.totalsByCurrency), ), @@ -306,15 +306,15 @@ export class PortfolioService { ]), metadataDependencies.map((dependency) => dependency.dataRevision), ]), - availability: missing ? - portfolios.length ? - 'partial' : - 'missing' : - 'complete', + availability: missing + ? portfolios.length + ? 'partial' + : 'missing' + : 'complete', fetchedAt: lastSuccessfulAt ? new Date(lastSuccessfulAt) : null, - freshForMs: portfolios.length ? - portfolioFreshForMs : - membershipFreshForMs, + freshForMs: portfolios.length + ? portfolioFreshForMs + : membershipFreshForMs, work: work.id ? { ...work, id: work.id } : null, dependencies: [ saved.membership, @@ -478,8 +478,8 @@ export class PortfolioService { const absoluteReturn = currentPrice && averagePurchasePrice && - currentPrice.currency === averagePurchasePrice.currency ? - { + currentPrice.currency === averagePurchasePrice.currency + ? { currency: currentPrice.currency, value: formatDecimal( multiply( @@ -490,8 +490,8 @@ export class PortfolioService { quantity, ), ), - } : - null; + } + : null; return { figi, instrumentUid, @@ -523,8 +523,8 @@ export class PortfolioService { private money(value: TbankMoney | undefined): MoneyDto | null { const currency = value?.currency?.trim().toUpperCase(); - return currency && value ? - { currency, value: formatDecimal(decimal(value)) } : - null; + return currency && value + ? { currency, value: formatDecimal(decimal(value)) } + : null; } } diff --git a/src/modules/investments/portfolio/domain/portfolio-structure.ts b/src/modules/investments/portfolio/domain/portfolio-structure.ts index cdc656f..6b01a9a 100644 --- a/src/modules/investments/portfolio/domain/portfolio-structure.ts +++ b/src/modules/investments/portfolio/domain/portfolio-structure.ts @@ -43,9 +43,9 @@ export function portfolioStructure( currencies.set(money.currency, group); }; for (const position of positions) { - const type = ['BOND', 'STOCK'].includes(position.type) ? - position.type : - 'OTHER'; + const type = ['BOND', 'STOCK'].includes(position.type) + ? position.type + : 'OTHER'; if (position.currentValue) include(type, position.currentValue); else unvalued.set(type, (unvalued.get(type) ?? INITIAL_UNVALUED_COUNT) + POSITION_INCREMENT); } @@ -65,8 +65,8 @@ export function portfolioStructure( value: formatDecimal(value), // Rounded to six decimal places using integer arithmetic. Only the // resulting dimensionless percentage is passed to chart geometry. - sharePercent: canCalculateShares ? - formatDecimal({ + sharePercent: canCalculateShares + ? formatDecimal({ value: (value.value * DECIMAL_RADIX ** BigInt(total.scale - value.scale) * @@ -74,8 +74,8 @@ export function portfolioStructure( total.value / ROUNDING_DIVISOR) / total.value, scale: SHARE_PERCENT_DECIMAL_PLACES, - }) : - null, + }) + : null, })); return { currency, diff --git a/src/modules/investments/portfolio/infrastructure/forecast.repository.ts b/src/modules/investments/portfolio/infrastructure/forecast.repository.ts index 8f77124..d7987db 100644 --- a/src/modules/investments/portfolio/infrastructure/forecast.repository.ts +++ b/src/modules/investments/portfolio/infrastructure/forecast.repository.ts @@ -58,9 +58,9 @@ export class ForecastRepository { where: { userId_revision_scopeKey: key }, }); const fallback = - previous?.membershipRevision === input.membershipRevision ? - previous : - null; + previous?.membershipRevision === input.membershipRevision + ? previous + : null; if (!input.candidate) return fallback; const portfolios = await tx.tbankPortfolio.findMany({ where: { diff --git a/src/modules/investments/tbank-sync/tbank-screen.ts b/src/modules/investments/tbank-sync/tbank-screen.ts index 99d47f4..ca228f0 100644 --- a/src/modules/investments/tbank-sync/tbank-screen.ts +++ b/src/modules/investments/tbank-sync/tbank-screen.ts @@ -100,23 +100,23 @@ export function normalizeScreen( ...(accountId ? { accountId } : {}), ...(input.from ? { from: input.from } : {}), ...(input.to ? { to: input.to } : {}), - ...(input.instrumentUid ? - { instrumentUid: input.instrumentUid.toLowerCase() } : - {}), + ...(input.instrumentUid + ? { instrumentUid: input.instrumentUid.toLowerCase() } + : {}), ...(query ? { query } : {}), ...(input.candlePeriod ? { candlePeriod: input.candlePeriod } : {}), - ...(input.candleRange ? - { + ...(input.candleRange + ? { candleRange: { from: new Date(input.candleRange.from).toISOString(), to: new Date(input.candleRange.to).toISOString(), }, - } : - {}), - ...(input.categories ? - // eslint-disable-next-line sonarjs/no-alphabetical-sort -- Code-unit ordering is intentional for canonical keys and ISO dates; locale collation would change persisted bindings. - { categories: [...new Set(input.categories)].sort() } : - {}), + } + : {}), + ...(input.categories + // eslint-disable-next-line sonarjs/no-alphabetical-sort -- Code-unit ordering is intentional for canonical keys and ISO dates; locale collation would change persisted bindings. + ? { categories: [...new Set(input.categories)].sort() } + : {}), ...(input.limit !== undefined ? { limit: input.limit } : {}), }; } diff --git a/src/modules/investments/tbank-sync/tbank-sync.repository.ts b/src/modules/investments/tbank-sync/tbank-sync.repository.ts index 8189dd0..4fe800a 100644 --- a/src/modules/investments/tbank-sync/tbank-sync.repository.ts +++ b/src/modules/investments/tbank-sync/tbank-sync.repository.ts @@ -79,9 +79,9 @@ export class TbankSyncRepository { work, children: children.map((child) => progress.attempts?.[child.id] && - progress.attempts[child.id] !== child.attemptId ? - { ...child, state: 'cancelled', errorCode: null } : - child, + progress.attempts[child.id] !== child.attemptId + ? { ...child, state: 'cancelled', errorCode: null } + : child, ), progress, }; diff --git a/src/modules/investments/tbank-sync/tbank-sync.service.ts b/src/modules/investments/tbank-sync/tbank-sync.service.ts index 0bb01da..dcd94e7 100644 --- a/src/modules/investments/tbank-sync/tbank-sync.service.ts +++ b/src/modules/investments/tbank-sync/tbank-sync.service.ts @@ -120,9 +120,9 @@ export class TbankSyncService { ) ) { for (const account of scope.screen === 'stock' || - scope.screen === 'bond' ? - current.accounts : - selectedAccounts) { + scope.screen === 'bond' + ? current.accounts + : selectedAccounts) { const work = await this.portfolio.request( userId, revision, @@ -135,9 +135,9 @@ export class TbankSyncService { } const saved = await this.portfolio.read( userId, - scope.screen === 'stock' || scope.screen === 'bond' ? - 'all' : - accountScope, + scope.screen === 'stock' || scope.screen === 'bond' + ? 'all' + : accountScope, true, ); const positions = saved.data?.positions ?? []; @@ -145,8 +145,8 @@ export class TbankSyncService { ...new Map( positions.flatMap((position) => position.instrumentUid && - (position.type === 'STOCK' || position.type === 'BOND') ? - [ + (position.type === 'STOCK' || position.type === 'BOND') + ? [ [ position.instrumentUid, { @@ -155,27 +155,27 @@ export class TbankSyncService { historyFrom: null, }, ] as const, - ] : - [], + ] + : [], ), ).values(), ]; const plan = planInvestmentScreen( { ...scope, - candleRange: scope.candleRange ? - { + candleRange: scope.candleRange + ? { from: new Date(scope.candleRange.from), to: new Date(scope.candleRange.to), - } : - undefined, + } + : undefined, }, { accounts: selectedAccounts, instruments: - scope.screen === 'stocks' || scope.screen === 'bonds' ? - [] : - instruments, + scope.screen === 'stocks' || scope.screen === 'bonds' + ? [] + : instruments, membershipKnown: Boolean(current.membershipAt), }, now, @@ -185,18 +185,18 @@ export class TbankSyncService { source.kind === 'operations' && source.accountId && source.from && - source.to ? - [ + source.to + ? [ { accountId: source.accountId, from: source.from, to: source.to, - ...(source.instrumentUid ? - { instrumentUid: source.instrumentUid } : - {}), + ...(source.instrumentUid + ? { instrumentUid: source.instrumentUid } + : {}), }, - ] : - [], + ] + : [], ); if (intervals.length) { await add([ @@ -278,11 +278,11 @@ export class TbankSyncService { [uid], true, [ - type === 'share' ? - 'dividends' : - scope.screen === 'bond' ? - 'coupons' : - 'bond-events', + type === 'share' + ? 'dividends' + : scope.screen === 'bond' + ? 'coupons' + : 'bond-events', ], { from: source.from.toISOString().slice(ISO_DATE_START, ISO_DATE_LENGTH), @@ -307,14 +307,14 @@ export class TbankSyncService { uid: scope.instrumentUid, instrumentType: scope.screen === 'bond' ? 'bond' : 'share', period: scope.candlePeriod ?? 'month', - ...(scope.candleRange ? - { + ...(scope.candleRange + ? { range: { from: new Date(scope.candleRange.from), to: new Date(scope.candleRange.to), }, - } : - {}), + } + : {}), }, now, true, @@ -346,9 +346,9 @@ export class TbankSyncService { await this.access.local(userId, intent.scope.accountId); const children = await Promise.all( saved.children.map((child) => - child.state === 'superseded' ? - this.operations.observe(userId, child.id) : - Promise.resolve(child), + child.state === 'superseded' + ? this.operations.observe(userId, child.id) + : Promise.resolve(child), ), ); const running = children.some((child) => child.state === 'running'); @@ -358,15 +358,15 @@ export class TbankSyncService { null; const state = saved.work.processRunId !== this.runId && - (!saved.progress.dispatched || running) ? - 'cancelled' : - !saved.progress.dispatched || running ? - 'running' : - failed ? - 'failed' : - children.some((child) => child.state === 'cancelled') ? - 'cancelled' : - 'succeeded'; + (!saved.progress.dispatched || running) + ? 'cancelled' + : !saved.progress.dispatched || running + ? 'running' + : failed + ? 'failed' + : children.some((child) => child.state === 'cancelled') + ? 'cancelled' + : 'succeeded'; const resources = await this.observe(userId, intent.scope); if ( (await this.access.local(userId, intent.scope.accountId)).revision !== @@ -423,9 +423,9 @@ export class TbankSyncService { typeof input.kind === 'string' ) { const range = - typeof input.rangeKey === 'string' && input.rangeKey.startsWith('[') ? - (JSON.parse(input.rangeKey) as string[]) : - null; + typeof input.rangeKey === 'string' && input.rangeKey.startsWith('[') + ? (JSON.parse(input.rangeKey) as string[]) + : null; const sources = await this.sources.readMany( userId, type, @@ -434,9 +434,9 @@ export class TbankSyncService { [ input.kind as Parameters< CatalogEnrichmentService['readMany'] - >[4] extends (infer K)[] | undefined ? - K : - never, + >[4] extends (infer K)[] | undefined + ? K + : never, ], range ? { from: range[0], to: range[1] } : undefined, ); @@ -492,19 +492,19 @@ export class TbankSyncService { ); } } - const observed = work.scopeKey.startsWith('operations:') ? - await this.operations.observe(userId, work.id) : - work; + const observed = work.scopeKey.startsWith('operations:') + ? await this.operations.observe(userId, work.id) + : work; const state = - observed.state === 'running' ? - observed.updatedAt < this.startedAt ? - 'cancelled' : - 'running' : - observed.state === 'failed' ? - 'failed' : - observed.state === 'cancelled' ? - 'cancelled' : - 'succeeded'; + observed.state === 'running' + ? observed.updatedAt < this.startedAt + ? 'cancelled' + : 'running' + : observed.state === 'failed' + ? 'failed' + : observed.state === 'cancelled' + ? 'cancelled' + : 'succeeded'; if ((await this.access.local(userId)).revision !== work.revision) { throw new NotFoundException(); } @@ -597,27 +597,27 @@ export class TbankSyncService { uid, instrumentType: type, period: scope.candlePeriod ?? 'month', - ...(scope.candleRange ? - { + ...(scope.candleRange + ? { range: { from: new Date(scope.candleRange.from), to: new Date(scope.candleRange.to), }, - } : - {}), + } + : {}), }, true, ), ); await add( - type === 'share' ? - this.stockIncome.readPosition(userId, uid, true) : - this.bondIncome.readPosition(userId, uid, true), + type === 'share' + ? this.stockIncome.readPosition(userId, uid, true) + : this.bondIncome.readPosition(userId, uid, true), ); await add( - type === 'share' ? - this.stockIncome.dividends(userId, uid, true) : - this.bondIncome.coupons(userId, uid, true), + type === 'share' + ? this.stockIncome.dividends(userId, uid, true) + : this.bondIncome.coupons(userId, uid, true), ); } return resources; diff --git a/src/modules/investments/upcoming-events/upcoming-events.service.ts b/src/modules/investments/upcoming-events/upcoming-events.service.ts index 5c208f3..dc30d92 100644 --- a/src/modules/investments/upcoming-events/upcoming-events.service.ts +++ b/src/modules/investments/upcoming-events/upcoming-events.service.ts @@ -154,15 +154,15 @@ export class UpcomingEventsService { }; const quantity = decimalFromString(position.quantity); const projected = - type === 'share' ? - (calendar.payload?.dividends ?? []) + type === 'share' + ? (calendar.payload?.dividends ?? []) .filter( (event) => event.dividendType?.toLowerCase() !== 'cancelled' && Boolean(event.recordDate), ) - .map((event) => dividendEvent(identity, quantity, event)) : - (calendar.payload?.bondEvents ?? []).map((event) => + .map((event) => dividendEvent(identity, quantity, event)) + : (calendar.payload?.bondEvents ?? []).map((event) => bondEvent(identity, quantity, event), ); events.push( @@ -172,9 +172,9 @@ export class UpcomingEventsService { ) .map((event) => ({ ...event, - ...(accountId === 'all' && position.account ? - { account: position.account } : - {}), + ...(accountId === 'all' && position.account + ? { account: position.account } + : {}), })), ); } @@ -185,14 +185,14 @@ export class UpcomingEventsService { a.type.localeCompare(b.type) || a.ticker.localeCompare(b.ticker), ); - const candidate: undefined | UpcomingEventsDto = ready ? - { + const candidate: undefined | UpcomingEventsDto = ready + ? { accountId, calculatedAt: new Date().toISOString(), events, incomeTotalsByCurrency: incomeTotals(events), - } : - undefined; + } + : undefined; return this.forecasts.result({ userId, revision: connection.revision, @@ -224,13 +224,13 @@ export function dividendEvent( .toISOString() .slice(ISO_DATE_START, ISO_DATE_LENGTH), dateKind: 'registry', - projectedAmount: event.dividendNet ? - projectedMoney( + projectedAmount: event.dividendNet + ? projectedMoney( quantity, formatDecimal(decimal(event.dividendNet)), event.dividendNet.currency, - ) : - null, + ) + : null, }; } @@ -248,9 +248,9 @@ export function bondEvent( date: event.date, dateKind: 'event', projectedAmount: - 'value' in event ? - projectedMoney(quantity, event.value, event.currency) : - null, + 'value' in event + ? projectedMoney(quantity, event.value, event.currency) + : null, }; } diff --git a/test/sqlite-regression.cjs b/test/sqlite-regression.cjs index e6e0c96..13e1298 100644 --- a/test/sqlite-regression.cjs +++ b/test/sqlite-regression.cjs @@ -64,13 +64,13 @@ async function check(name, overrides = {}, signal) { 'user database must be untouched', ); const expected = - signal === 'SIGINT' ? - 130 : - signal === 'SIGTERM' ? - 143 : - overrides.PRISMA_SCHEMA_ENGINE_BINARY ? - 1 : - 0; + signal === 'SIGINT' + ? 130 + : signal === 'SIGTERM' + ? 143 + : overrides.PRISMA_SCHEMA_ENGINE_BINARY + ? 1 + : 0; assert.equal(code, expected, `See ${name}.log`); console.log(`PASS ${name}: ${dirs.length} isolated databases`); } diff --git a/test/sqlite.cjs b/test/sqlite.cjs index 4bcedfc..6d13fe0 100644 --- a/test/sqlite.cjs +++ b/test/sqlite.cjs @@ -9,11 +9,11 @@ const available = readdirSync(__dirname) .sort(); const requested = process.argv.slice(2); const suites = - requested.length === 0 ? - ['app.e2e-spec.ts', 'bank-cards.e2e-spec.ts'] : - requested.length === 1 && requested[0] === '--all' ? - available : - requested; + requested.length === 0 + ? ['app.e2e-spec.ts', 'bank-cards.e2e-spec.ts'] + : requested.length === 1 && requested[0] === '--all' + ? available + : requested; if (suites.some((suite) => !available.includes(suite))) { throw new Error('Unknown e2e suite'); } diff --git a/test/tbank-ledger-local.e2e-spec.ts b/test/tbank-ledger-local.e2e-spec.ts index 00e0036..9f9d699 100644 --- a/test/tbank-ledger-local.e2e-spec.ts +++ b/test/tbank-ledger-local.e2e-spec.ts @@ -240,9 +240,9 @@ describe('persistent ledger HTTP and cursor (SQLite)', () => { data: { occurredAt: new Date(observedAt.valueOf() - 1000) }, }); const query = - periodKind === 'default' ? - '' : - `&to=${observedAt.toISOString().slice(0, 10)}${periodKind === 'today timestamp' ? 'T23:59:59.999Z' : ''}`; + periodKind === 'default' + ? '' + : `&to=${observedAt.toISOString().slice(0, 10)}${periodKind === 'today timestamp' ? 'T23:59:59.999Z' : ''}`; const page = (cursor?: string, observe = false) => request(app.getHttpServer()) .get( diff --git a/test/tbank-local-reads.e2e-spec.ts b/test/tbank-local-reads.e2e-spec.ts index 3536b66..8f4d307 100644 --- a/test/tbank-local-reads.e2e-spec.ts +++ b/test/tbank-local-reads.e2e-spec.ts @@ -339,9 +339,9 @@ describe('local accounts and portfolio HTTP (SQLite)', () => { ]); await put().expect(200); upstream.getPortfolio.mockImplementation((_token, id) => - id === 'second' ? - Promise.reject(new Error('synthetic unavailable')) : - Promise.resolve({ + id === 'second' + ? Promise.reject(new Error('synthetic unavailable')) + : Promise.resolve({ positions: [ { instrumentUid: uid, diff --git a/test/tbank-metadata-persistence.e2e-spec.ts b/test/tbank-metadata-persistence.e2e-spec.ts index ef509bf..5e0dbea 100644 --- a/test/tbank-metadata-persistence.e2e-spec.ts +++ b/test/tbank-metadata-persistence.e2e-spec.ts @@ -351,9 +351,9 @@ describe('durable shared instrument metadata (SQLite)', () => { await service.read('owner', identity); await settled(); const identities = Array.from({ length: 1000 }, (_, index) => - index ? - { instrumentUid: `uid-${index}`, figi: `FIGI-${index}` } : - identity, + index + ? { instrumentUid: `uid-${index}`, figi: `FIGI-${index}` } + : identity, ); const saved = await service.savedMany(identities); expect(saved.size).toBe(1000); diff --git a/test/tbank-read-baseline.e2e-spec.ts b/test/tbank-read-baseline.e2e-spec.ts index 5a16f08..b3388b4 100644 --- a/test/tbank-read-baseline.e2e-spec.ts +++ b/test/tbank-read-baseline.e2e-spec.ts @@ -58,9 +58,9 @@ describe('T-Bank persisted read budget', () => { const operations = Array.from({ length: 100_000 }, (_, index) => { // 1,000 measured-month rows, 200 income receipts, remaining rows over 10 years. const time = - index < 1000 ? - Date.UTC(2026, 7, 1) + index * 2_500_000 : - Date.UTC(2016, 8, 1) + (index - 1000) * 3_160_000; + index < 1000 + ? Date.UTC(2026, 7, 1) + index * 2_500_000 + : Date.UTC(2016, 8, 1) + (index - 1000) * 3_160_000; const instrument = instruments[index % instruments.length]; return { id: `operation-${index}`, @@ -298,9 +298,9 @@ describe('T-Bank persisted read budget', () => { uid: instrument.uid, ticker: instrument.ticker, instrumentType: - instrument.instrumentKind === 'INSTRUMENT_TYPE_SHARE' ? - 'share' : - 'bond', + instrument.instrumentKind === 'INSTRUMENT_TYPE_SHARE' + ? 'share' + : 'bond', metadata: instrument, fetchedAt: now, }, @@ -487,13 +487,13 @@ describe('T-Bank persisted read budget', () => { runtime: process.version, hardware: { cpuQuota: - process.platform === 'linux' ? - readFileSync('/sys/fs/cgroup/cpu.max', 'utf8').trim() : - null, + process.platform === 'linux' + ? readFileSync('/sys/fs/cgroup/cpu.max', 'utf8').trim() + : null, memoryLimit: - process.platform === 'linux' ? - readFileSync('/sys/fs/cgroup/memory.max', 'utf8').trim() : - null, + process.platform === 'linux' + ? readFileSync('/sys/fs/cgroup/memory.max', 'utf8').trim() + : null, cpu: cpus()[0]?.model, cpus: cpus().length, memoryBytes: totalmem(), diff --git a/test/tbank-screen-sync.e2e-spec.ts b/test/tbank-screen-sync.e2e-spec.ts index f319d78..bf3c590 100644 --- a/test/tbank-screen-sync.e2e-spec.ts +++ b/test/tbank-screen-sync.e2e-spec.ts @@ -358,9 +358,9 @@ describe('screen sync HTTP and SQLite', () => { }), ) .mockImplementation(() => - outcome === 'failed' ? - Promise.reject(new Error('Synthetic outage')) : - Promise.resolve({ hasNext: false, items: [] }), + outcome === 'failed' + ? Promise.reject(new Error('Synthetic outage')) + : Promise.resolve({ hasNext: false, items: [] }), ); const accept = async (from: string, to: string) => { const response = await post({ diff --git a/test/upcoming-events.e2e-spec.ts b/test/upcoming-events.e2e-spec.ts index d19ee82..4a23c8d 100644 --- a/test/upcoming-events.e2e-spec.ts +++ b/test/upcoming-events.e2e-spec.ts @@ -296,9 +296,9 @@ describe('saved payout calendars and complete forecasts (SQLite HTTP)', () => { .body as UpcomingEventsResourceDto; await portfolio([position(shareUid, '7'), position(newUid, '5')]); upstream.getDividends.mockImplementation((_token: string, uid: string) => - uid === newUid ? - Promise.reject(new Error('new calendar unavailable')) : - Promise.resolve([]), + uid === newUid + ? Promise.reject(new Error('new calendar unavailable')) + : Promise.resolve([]), ); const pending = (await events(false).expect(200)) .body as UpcomingEventsResourceDto;