From 8323d984f58c2c15e6e6ac77301c065fa5f94bbc Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:28:43 +0100 Subject: [PATCH 1/9] feat: add stellar error definitions --- .../src/common/errors/stellar.errors.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 nftopia-backend/src/common/errors/stellar.errors.ts diff --git a/nftopia-backend/src/common/errors/stellar.errors.ts b/nftopia-backend/src/common/errors/stellar.errors.ts new file mode 100644 index 0000000..3022c00 --- /dev/null +++ b/nftopia-backend/src/common/errors/stellar.errors.ts @@ -0,0 +1,41 @@ +export class StellarError extends Error { + constructor( + message: string, + public readonly code: string, + public readonly metadata?: Record, + ) { + super(message); + this.name = this.constructor.name; + Error.captureStackTrace(this, this.constructor); + } +} + +export class SorobanRpcError extends StellarError { + constructor(message: string, metadata?: Record) { + super(message, 'SOROBAN_RPC_ERROR', metadata); + } +} + +export class TransactionFailedError extends StellarError { + constructor(message: string, metadata?: Record) { + super(message, 'TRANSACTION_FAILED_ERROR', metadata); + } +} + +export class InsufficientBalanceError extends StellarError { + constructor(message: string, metadata?: Record) { + super(message, 'INSUFFICIENT_BALANCE_ERROR', metadata); + } +} + +export class InvalidSignatureError extends StellarError { + constructor(message: string, metadata?: Record) { + super(message, 'INVALID_SIGNATURE_ERROR', metadata); + } +} + +export class ContractError extends StellarError { + constructor(message: string, metadata?: Record) { + super(message, 'CONTRACT_ERROR', metadata); + } +} From e7974e4f4cbe1b3f176f848704fac6e41bdb7b97 Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:28:44 +0100 Subject: [PATCH 2/9] feat: add stellar configuration for timeouts and RPC --- nftopia-backend/src/config/stellar.config.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 nftopia-backend/src/config/stellar.config.ts diff --git a/nftopia-backend/src/config/stellar.config.ts b/nftopia-backend/src/config/stellar.config.ts new file mode 100644 index 0000000..fcc134c --- /dev/null +++ b/nftopia-backend/src/config/stellar.config.ts @@ -0,0 +1,20 @@ +import { registerAs } from '@nestjs/config'; + +export default registerAs('stellar', () => ({ + network: process.env.STELLAR_NETWORK || 'testnet', + rpcUrl: process.env.SOROBAN_RPC_URL || 'https://soroban-testnet.stellar.org', + passphrase: + process.env.STELLAR_NETWORK_PASSPHRASE || + 'Test SDF Network ; September 2015', + timeouts: { + rpcCall: parseInt(process.env.STELLAR_RPC_TIMEOUT_MS || '30000', 10), + simulation: parseInt( + process.env.STELLAR_SIMULATION_TIMEOUT_MS || '15000', + 10, + ), + submission: parseInt( + process.env.STELLAR_SUBMISSION_TIMEOUT_MS || '45000', + 10, + ), + }, +})); From 5b289a04936f756a961b8d147308f9da66c84ffd Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:28:46 +0100 Subject: [PATCH 3/9] feat: add stellar response interceptor --- .../stellar-response.interceptor.ts | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 nftopia-backend/src/interceptors/stellar-response.interceptor.ts diff --git a/nftopia-backend/src/interceptors/stellar-response.interceptor.ts b/nftopia-backend/src/interceptors/stellar-response.interceptor.ts new file mode 100644 index 0000000..a5bc4d0 --- /dev/null +++ b/nftopia-backend/src/interceptors/stellar-response.interceptor.ts @@ -0,0 +1,67 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ConfigService } from '@nestjs/config'; + +export interface StellarResponse { + data: T; + meta: { + timestamp: string; + network: string; + transactionHash?: string; + contractId?: string; + }; +} + +@Injectable() +export class StellarResponseInterceptor implements NestInterceptor< + T, + StellarResponse +> { + constructor(private configService: ConfigService) {} + + intercept( + context: ExecutionContext, + next: CallHandler, + ): Observable> { + const network = + this.configService.get('stellar.network') || 'testnet'; + + return next.handle().pipe( + map((data) => { + let txHash, + contractId, + actualData = data; + + if (data && typeof data === 'object') { + if ('transactionHash' in data) { + txHash = data.transactionHash; + } + if ('contractId' in data) { + contractId = data.contractId; + } + // If the payload explicitly returned 'data' separate from metadata, use it. Otherwise, use the whole object. + if ('data' in data && Object.keys(data).length <= 3) { + // rudimentary check + actualData = data.data; + } + } + + return { + data: actualData, + meta: { + timestamp: new Date().toISOString(), + network, + ...(txHash && { transactionHash: txHash }), + ...(contractId && { contractId }), + }, + }; + }), + ); + } +} From 3e0458015bea03d9ec735b53d28d8a9e1e4f66db Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:28:47 +0100 Subject: [PATCH 4/9] feat: add stellar logging interceptor --- .../stellar-logging.interceptor.ts | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 nftopia-backend/src/interceptors/stellar-logging.interceptor.ts diff --git a/nftopia-backend/src/interceptors/stellar-logging.interceptor.ts b/nftopia-backend/src/interceptors/stellar-logging.interceptor.ts new file mode 100644 index 0000000..11d77db --- /dev/null +++ b/nftopia-backend/src/interceptors/stellar-logging.interceptor.ts @@ -0,0 +1,58 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, + Logger, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { Request } from 'express'; + +@Injectable() +export class StellarLoggingInterceptor implements NestInterceptor { + private readonly logger = new Logger('StellarTx'); + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const ctx = context.switchToHttp(); + const req = ctx.getRequest(); + const now = Date.now(); + const method = req.method; + const url = req.url; + + // We can infer Soroban specific activity based on endpoint routes + const isStellarRoute = url.includes('/nft') || url.includes('/marketplace'); + + if (isStellarRoute) { + if (process.env.NODE_ENV !== 'production') { + this.logger.debug(`[SorobanRPC] Incoming request: ${method} ${url}`); + } + } + + return next.handle().pipe( + tap({ + next: (data: any) => { + const delay = Date.now() - now; + if (data && typeof data === 'object') { + const txHash = data.transactionHash || data.meta?.transactionHash; + const contractId = data.contractId || data.meta?.contractId; + + if (txHash || contractId) { + this.logger.log( + `[StellarTx] ${method} ${url} +${delay}ms - TX: ${txHash || 'N/A'} Contract: ${contractId || 'N/A'}`, + ); + } else if (isStellarRoute) { + this.logger.log(`[StellarCall] ${method} ${url} +${delay}ms`); + } + } + }, + error: (error: any) => { + const delay = Date.now() - now; + this.logger.error( + `[StellarError] ${method} ${url} +${delay}ms - ${error.message}`, + ); + }, + }), + ); + } +} From b034ab4919734db5733c502002d3fe24ecbd7d9f Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:28:48 +0100 Subject: [PATCH 5/9] feat: add stellar error interceptor --- .../interceptors/stellar-error.interceptor.ts | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 nftopia-backend/src/interceptors/stellar-error.interceptor.ts diff --git a/nftopia-backend/src/interceptors/stellar-error.interceptor.ts b/nftopia-backend/src/interceptors/stellar-error.interceptor.ts new file mode 100644 index 0000000..5119502 --- /dev/null +++ b/nftopia-backend/src/interceptors/stellar-error.interceptor.ts @@ -0,0 +1,72 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, + BadGatewayException, + BadRequestException, +} from '@nestjs/common'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { + SorobanRpcError, + TransactionFailedError, + InsufficientBalanceError, + InvalidSignatureError, + ContractError, +} from '../common/errors/stellar.errors'; + +@Injectable() +export class StellarErrorInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + catchError((error: any) => { + let safeMessage = error.message; + if (safeMessage && typeof safeMessage === 'string') { + // Redact Stellar secret seeds (starts with S and is 56 chars long) + safeMessage = safeMessage.replace( + /(S[A-Z0-9]{55})/g, + '[REDACTED_SECRET]', + ); + } + + if (error instanceof SorobanRpcError) { + return throwError( + () => + new BadGatewayException({ + message: safeMessage, + code: error.code, + ...error.metadata, + }), + ); + } + if (error instanceof TransactionFailedError) { + return throwError( + () => + new BadRequestException({ + message: safeMessage, + code: error.code, + ...error.metadata, + }), + ); + } + if ( + error instanceof InsufficientBalanceError || + error instanceof InvalidSignatureError || + error instanceof ContractError + ) { + return throwError( + () => + new BadRequestException({ + message: safeMessage, + code: error.code, + ...error.metadata, + }), + ); + } + + return throwError(() => error); + }), + ); + } +} From 17cd2707e4d8aafa0f1af6f40b461ce72994c987 Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:28:56 +0100 Subject: [PATCH 6/9] feat: add stellar timeout interceptor --- .../stellar-timeout.interceptor.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 nftopia-backend/src/interceptors/stellar-timeout.interceptor.ts diff --git a/nftopia-backend/src/interceptors/stellar-timeout.interceptor.ts b/nftopia-backend/src/interceptors/stellar-timeout.interceptor.ts new file mode 100644 index 0000000..b4f59bd --- /dev/null +++ b/nftopia-backend/src/interceptors/stellar-timeout.interceptor.ts @@ -0,0 +1,47 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, + RequestTimeoutException, +} from '@nestjs/common'; +import { Observable, throwError, TimeoutError } from 'rxjs'; +import { catchError, timeout } from 'rxjs/operators'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class StellarTimeoutInterceptor implements NestInterceptor { + constructor(private configService: ConfigService) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const defaultTimeout = + this.configService.get('stellar.timeouts.rpcCall') || 30000; + + const req = context.switchToHttp().getRequest(); + const url = req.url; + let timeoutMs = defaultTimeout; + + if (url.includes('/simulate')) { + timeoutMs = + this.configService.get('stellar.timeouts.simulation') || 15000; + } else if (url.includes('/submit')) { + timeoutMs = + this.configService.get('stellar.timeouts.submission') || 45000; + } + + return next.handle().pipe( + timeout(timeoutMs), + catchError((err: any) => { + if (err instanceof TimeoutError) { + return throwError( + () => + new RequestTimeoutException( + `Stellar action timed out after ${timeoutMs}ms`, + ), + ); + } + return throwError(() => err); + }), + ); + } +} From c231f664c99401ce77eba727e0f12358a749397d Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:28:56 +0100 Subject: [PATCH 7/9] feat: add stellar transform interceptor --- .../stellar-transform.interceptor.ts | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 nftopia-backend/src/interceptors/stellar-transform.interceptor.ts diff --git a/nftopia-backend/src/interceptors/stellar-transform.interceptor.ts b/nftopia-backend/src/interceptors/stellar-transform.interceptor.ts new file mode 100644 index 0000000..8c11868 --- /dev/null +++ b/nftopia-backend/src/interceptors/stellar-transform.interceptor.ts @@ -0,0 +1,98 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +@Injectable() +export class StellarTransformInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + return next.handle().pipe( + map((data) => { + return this.transformData(data); + }), + ); + } + + private transformData(data: any): any { + if (!data) return data; + + // Handle Horizon server collection pages + if (data.records && Array.isArray(data.records)) { + return { + items: data.records.map((item: any) => this.transformItem(item)), + nextPageToken: this.extractNextPageToken(data), + }; + } + + // Handle arrays + if (Array.isArray(data)) { + return data.map((item: any) => this.transformItem(item)); + } + + // Handle single item explicitly if it's an object with `data` payload + if (data.data && typeof data.data === 'object') { + data.data = this.transformData(data.data); + return data; + } + + return this.transformItem(data); + } + + private extractNextPageToken(data: any): string | null { + if (data._links && data._links.next && data._links.next.href) { + try { + const nextUrl = new URL(data._links.next.href); + return nextUrl.searchParams.get('cursor') || null; + } catch (e) { + return null; + } + } + return null; + } + + private stroopsToXlm(stroops: string): string { + try { + const isInteger = /^\d+$/.test(stroops); + if (!isInteger) return stroops; // Already formatted or invalid + + const s = BigInt(stroops); + const xlm = s / 10000000n; + const fraction = s % 10000000n; + if (fraction === 0n) return xlm.toString(); + const fractionStr = fraction + .toString() + .padStart(7, '0') + .replace(/0+$/, ''); + return `${xlm}.${fractionStr}`; + } catch { + return stroops; + } + } + + private transformItem(item: any): any { + if (!item || typeof item !== 'object') return item; + + const transformed = { ...item }; + + // Format Stellar account responses (e.g., balances, trustlines). + if (transformed.balances && Array.isArray(transformed.balances)) { + transformed.balances = transformed.balances.map((b: any) => { + if ( + b.balance && + typeof b.balance === 'string' && + /^\d+$/.test(b.balance) + ) { + // If we suspect it's pure stroops (no decimal), convert it to XLM format string + b.balanceXlm = this.stroopsToXlm(b.balance); + } + return b; + }); + } + + return transformed; + } +} From ab200aa320c0835f295e9b409f3116a1134a9dc4 Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:28:57 +0100 Subject: [PATCH 8/9] feat: wire interceptors into main app and config module --- nftopia-backend/src/app.module.ts | 3 ++- nftopia-backend/src/main.ts | 15 +++++++++++++++ nftopia-backend/src/nft/soroban.service.ts | 10 +++++++--- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/nftopia-backend/src/app.module.ts b/nftopia-backend/src/app.module.ts index db9933f..bf5d4e7 100644 --- a/nftopia-backend/src/app.module.ts +++ b/nftopia-backend/src/app.module.ts @@ -11,6 +11,7 @@ import { NftModule } from './nft/nft.module'; import { LoggerModule } from 'nestjs-pino'; import { APP_FILTER } from '@nestjs/core'; import { HttpExceptionFilter } from './common/filters/http-exception.filter'; +import stellarConfig from './config/stellar.config'; @Module({ imports: [ @@ -38,7 +39,7 @@ import { HttpExceptionFilter } from './common/filters/http-exception.filter'; }, }), }), - ConfigModule.forRoot({ isGlobal: true }), + ConfigModule.forRoot({ isGlobal: true, load: [stellarConfig] }), CacheModule.registerAsync({ isGlobal: true, inject: [ConfigService], diff --git a/nftopia-backend/src/main.ts b/nftopia-backend/src/main.ts index 6974fd9..a3ce1ce 100644 --- a/nftopia-backend/src/main.ts +++ b/nftopia-backend/src/main.ts @@ -4,6 +4,12 @@ import { AppModule } from './app.module'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { ValidationPipe } from '@nestjs/common'; import { Logger } from 'nestjs-pino'; +import { ConfigService } from '@nestjs/config'; +import { StellarResponseInterceptor } from './interceptors/stellar-response.interceptor'; +import { StellarLoggingInterceptor } from './interceptors/stellar-logging.interceptor'; +import { StellarErrorInterceptor } from './interceptors/stellar-error.interceptor'; +import { StellarTimeoutInterceptor } from './interceptors/stellar-timeout.interceptor'; +import { StellarTransformInterceptor } from './interceptors/stellar-transform.interceptor'; async function bootstrap() { const app = await NestFactory.create(AppModule); @@ -26,6 +32,15 @@ async function bootstrap() { }), ); + const configService = app.get(ConfigService); + app.useGlobalInterceptors( + new StellarLoggingInterceptor(), + new StellarTimeoutInterceptor(configService), + new StellarErrorInterceptor(), + new StellarTransformInterceptor(), + new StellarResponseInterceptor(configService), + ); + // Set global API prefix app.setGlobalPrefix('api/v1'); diff --git a/nftopia-backend/src/nft/soroban.service.ts b/nftopia-backend/src/nft/soroban.service.ts index 5992ac8..aff6caa 100644 --- a/nftopia-backend/src/nft/soroban.service.ts +++ b/nftopia-backend/src/nft/soroban.service.ts @@ -2,6 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Server, Durability } from 'stellar-sdk/rpc'; import { xdr } from 'stellar-sdk'; +import { SorobanRpcError } from '../common/errors/stellar.errors'; @Injectable() export class SorobanService { @@ -36,7 +37,10 @@ export class SorobanService { `Failed to fetch contract data for contract ${contractId}: ${error.message}`, error.stack, ); - return null; + throw new SorobanRpcError( + `Contract data fetch failed: ${error.message}`, + { contractId }, + ); } } @@ -60,7 +64,7 @@ export class SorobanService { } catch (e) { const error = e as Error; this.logger.error(`Error fetching events: ${error.message}`); - return []; + throw new SorobanRpcError(`Events fetch failed: ${error.message}`); } } @@ -71,7 +75,7 @@ export class SorobanService { } catch (e) { const error = e as Error; this.logger.error(`Error fetching latest ledger: ${error.message}`); - return 0; + throw new SorobanRpcError(`Latest ledger fetch failed: ${error.message}`); } } } From a88e2e0ff8a8ceb357014b3f228fbcba2b376051 Mon Sep 17 00:00:00 2001 From: Caxton22 Date: Sat, 21 Feb 2026 11:38:55 +0100 Subject: [PATCH 9/9] file: package.json --- nftopia-backend/package-lock.json | 37 +++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/nftopia-backend/package-lock.json b/nftopia-backend/package-lock.json index c5aee51..f15046b 100644 --- a/nftopia-backend/package-lock.json +++ b/nftopia-backend/package-lock.json @@ -243,6 +243,7 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -2183,6 +2184,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.12.tgz", "integrity": "sha512-v6U3O01YohHO+IE3EIFXuRuu3VJILWzyMmSYZXpyBbnp0hk0mFyHxK2w3dF4I5WnbwiRbWlEXdeXFvPQ7qaZzw==", "license": "MIT", + "peer": true, "dependencies": { "file-type": "21.3.0", "iterare": "1.2.1", @@ -2248,6 +2250,7 @@ "integrity": "sha512-97DzTYMf5RtGAVvX1cjwpKRiCUpkeQ9CCzSAenqkAhOmNVVFaApbhuw+xrDt13rsCa2hHVOYPrV4dBgOYMJjsA==", "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@nuxt/opencollective": "0.4.1", "fast-safe-stringify": "2.1.1", @@ -2331,6 +2334,7 @@ "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.12.tgz", "integrity": "sha512-GYK/vHI0SGz5m8mxr7v3Urx8b9t78Cf/dj5aJMZlGd9/1D9OI1hAl00BaphjEXINUJ/BQLxIlF2zUjrYsd6enQ==", "license": "MIT", + "peer": true, "dependencies": { "cors": "2.8.5", "express": "5.2.1", @@ -2620,6 +2624,7 @@ "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", "license": "MIT", + "peer": true, "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -2930,6 +2935,7 @@ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -3064,6 +3070,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -3244,6 +3251,7 @@ "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", @@ -3925,6 +3933,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "devOptional": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3974,6 +3983,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4483,6 +4493,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -4582,6 +4593,7 @@ "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-7.2.8.tgz", "integrity": "sha512-0HDaDLBBY/maa/LmUVAr70XUOwsiQD+jyzCBjmUErYZUKdMS9dT59PqW59PpVqfGM7ve6H0J6307JTpkCYefHQ==", "license": "MIT", + "peer": true, "dependencies": { "@cacheable/utils": "^2.3.3", "keyv": "^5.5.5" @@ -4727,6 +4739,7 @@ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -4774,13 +4787,15 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/class-validator": { "version": "0.14.3", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", "license": "MIT", + "peer": true, "dependencies": { "@types/validator": "^13.15.3", "libphonenumber-js": "^1.11.1", @@ -5518,6 +5533,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5578,6 +5594,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -5819,6 +5836,7 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -6970,6 +6988,7 @@ "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "30.2.0", "@jest/types": "30.2.0", @@ -7857,6 +7876,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -8655,6 +8675,7 @@ "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", "license": "MIT", + "peer": true, "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", @@ -8772,6 +8793,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.17.2.tgz", "integrity": "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.10.1", "pg-pool": "^3.11.0", @@ -8881,6 +8903,7 @@ "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz", "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==", "license": "MIT", + "peer": true, "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", @@ -8912,6 +8935,7 @@ "resolved": "https://registry.npmjs.org/pino-http/-/pino-http-11.0.0.tgz", "integrity": "sha512-wqg5XIAGRRIWtTk8qPGxkbrfiwEWz1lgedVLvhLALudKXvg1/L2lTFgTGPJ4Z2e3qcRmxoFxDuSdMdMGNM6I1g==", "license": "MIT", + "peer": true, "dependencies": { "get-caller-file": "^2.0.5", "pino": "^10.0.0", @@ -9114,6 +9138,7 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -9356,7 +9381,8 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/require-addon": { "version": "1.2.0", @@ -9465,6 +9491,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -10205,6 +10232,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -10564,6 +10592,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -10730,6 +10759,7 @@ "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", "license": "MIT", + "peer": true, "dependencies": { "@sqltools/formatter": "^1.2.5", "ansis": "^4.2.0", @@ -10935,6 +10965,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11220,6 +11251,7 @@ "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -11289,6 +11321,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1",