fail safe

This commit is contained in:
Donat Magda
2025-11-25 23:36:12 +01:00
parent 0566b58240
commit 1c1792e7e1
25 changed files with 10 additions and 8946 deletions
Generated Vendored Regular → Executable
View File
-285
View File
@@ -1,285 +0,0 @@
import express from 'express';
import { createServer } from 'http';
import cookieParser from 'cookie-parser';
import helmet from 'helmet';
import { AppDataSource } from '../Infrastructure/ormconfig';
import userRouter from './routers/userRouter';
import organizationRouter from './routers/organizationRouter';
import deckRouter from './routers/deckRouter';
import chatRouter from './routers/chatRouter';
import contactRouter from './routers/contactRouter';
import adminRouter from './routers/adminRouter';
import deckImportExportRouter from './routers/deckImportExportRouter';
import gameRouter from './routers/gameRouter';
import { LoggingService, logStartup, logConnection, logError, logRequest } from '../Application/Services/Logger';
import { WebSocketService } from '../Application/Services/WebSocketService';
import { GameWebSocketService } from '../Application/Services/GameWebSocketService';
import { container } from '../Application/Services/DIContainer';
import { GameRepository } from '../Infrastructure/Repository/GameRepository';
import { UserRepository } from '../Infrastructure/Repository/UserRepository';
import { RedisService } from '../Application/Services/RedisService';
import { setupSwagger } from './swagger/swaggerUiSetup';
const app = express();
const httpServer = createServer(app);
const PORT = process.env.PORT || 3000;
const isDevelopment = process.env.NODE_ENV === 'development';
const loggingService = LoggingService.getInstance();
logStartup('SerpentRace Backend starting up', {
environment: process.env.NODE_ENV || 'development',
port: PORT,
nodeVersion: process.version,
chatInactivityTimeout: process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30'
});
app.use(helmet({
contentSecurityPolicy: isDevelopment ? false : undefined
}));
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
app.use(cookieParser());
app.use(loggingService.requestLoggingMiddleware());
app.use((req, res, next) => {
const origin = req.headers.origin;
const allowedOrigins = ['http://localhost:3000', 'http://localhost:3001', 'http://localhost:8080', process.env.FRONTEND_URL];
if (!origin || allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin || '*');
}
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Cookie');
if (req.method === 'OPTIONS') {
res.status(200).end();
return;
}
next();
});
if (isDevelopment) {
app.use((req, res, next) => {
logRequest(`${req.method} ${req.path}`, req, res);
next();
});
}
// Setup Swagger documentation
setupSwagger(app);
app.get('/', (req, res) => {
res.json({
service: 'SerpentRace Backend API',
status: 'running',
version: '1.0.0',
endpoints: {
swagger: '/api-docs',
users: '/api/users',
organizations: '/api/organizations',
decks: '/api/decks',
chats: '/api/chats',
contacts: '/api/contacts',
admin: '/api/admin',
deckImportExport: '/api/deck-import-export',
health: '/health'
},
websocket: {
enabled: true,
events: [
'chat:join', 'chat:leave', 'message:send',
'group:create', 'chat:direct', 'game:chat:create',
'chat:history'
]
}
});
});
app.get('/health', async (req, res) => {
try {
const isDbConnected = AppDataSource.isInitialized;
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
service: 'SerpentRace Backend API',
version: '1.0.0',
environment: process.env.NODE_ENV || 'development',
database: {
connected: isDbConnected,
type: AppDataSource.options.type
},
websocket: {
enabled: true
},
uptime: process.uptime()
});
} catch (error) {
res.status(503).json({
status: 'unhealthy',
timestamp: new Date().toISOString(),
error: 'Service health check failed'
});
}
});
// API Routes
app.use('/api/users', userRouter);
app.use('/api/organizations', organizationRouter);
app.use('/api/decks', deckRouter);
app.use('/api/chats', chatRouter);
app.use('/api/contacts', contactRouter);
app.use('/api/admin', adminRouter);
app.use('/api/deck-import-export', deckImportExportRouter);
app.use('/api/games', gameRouter);
// Global error handler (must be after routes)
app.use(loggingService.errorLoggingMiddleware());
app.use((error: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
logError('Global error handler caught unhandled error', error, req, res);
// Don't expose internal error details in production
const isDevelopment = process.env.NODE_ENV === 'development';
res.status(500).json({
error: 'Internal server error',
timestamp: new Date().toISOString(),
...(isDevelopment && { details: error.message, stack: error.stack })
});
});
// Handle 404 routes
app.use((req: express.Request, res: express.Response) => {
res.status(404).json({
error: 'Route not found',
path: req.originalUrl,
method: req.method,
timestamp: new Date().toISOString()
});
});
// Initialize WebSocket service after database connection
let webSocketService: WebSocketService;
let gameWebSocketService: GameWebSocketService;
let server: any; // Declare server variable
// Initialize database connection and start server
AppDataSource.initialize()
.then(() => {
const dbOptions = AppDataSource.options as any;
logConnection('Database connection established', 'postgresql', 'success', {
type: dbOptions.type,
host: dbOptions.host,
database: dbOptions.database
});
// Initialize WebSocket service after database is connected
webSocketService = new WebSocketService(httpServer);
logStartup('WebSocket service initialized', {
chatInactivityTimeout: process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30'
});
// Initialize Game WebSocket service for /game namespace via DIContainer
container.setSocketIO(webSocketService['io']);
gameWebSocketService = container.gameWebSocketService;
logStartup('Game WebSocket service initialized for /game namespace');
// Restore active games from snapshots (if any exist)
gameWebSocketService.restoreAllActiveGames()
.then(restoredCount => {
if (restoredCount > 0) {
logStartup(`Restored ${restoredCount} active game(s) from snapshots`);
}
})
.catch(error => {
logError('Failed to restore games from snapshots', error);
});
// Start server with WebSocket support AFTER database is ready
server = httpServer.listen(PORT, () => {
logStartup('Server started successfully', {
port: PORT,
environment: process.env.NODE_ENV || 'development',
timestamp: new Date().toISOString(),
endpoints: {
health: `/health`,
swagger: `/api-docs`,
users: `/api/users`,
organizations: `/api/organizations`,
decks: `/api/decks`,
chats: `/api/chats`
},
websocket: {
enabled: true,
chatInactivityTimeout: `${process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES || '30'} minutes`
}
});
});
})
.catch((error) => {
const dbOptions = AppDataSource.options as any;
logConnection('Database connection failed', 'postgresql', 'failure', {
error: error.message,
type: dbOptions.type,
host: dbOptions.host,
database: dbOptions.database
});
process.exit(1);
});
// Graceful shutdown
const gracefulShutdown = async (signal: string) => {
logStartup(`Received ${signal}. Shutting down gracefully...`);
// Snapshot all active games before shutdown
if (gameWebSocketService) {
try {
const snapshotCount = await gameWebSocketService.snapshotAllActiveGames();
logStartup(`Created ${snapshotCount} game snapshot(s) before shutdown`);
} catch (error) {
logError('Failed to snapshot games before shutdown', error as Error);
}
}
server.close(() => {
logStartup('HTTP server closed');
if (AppDataSource.isInitialized) {
AppDataSource.destroy()
.then(() => {
logConnection('Database connection closed', 'postgresql', 'success');
process.exit(0);
})
.catch((error) => {
logError('Error during database shutdown', error);
process.exit(1);
});
} else {
process.exit(0);
}
});
};
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
logError('Uncaught Exception - Server will shut down', error);
process.exit(1);
});
// Handle unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
logError('Unhandled Rejection - Server will shut down', new Error(String(reason)), undefined, undefined);
process.exit(1);
});
// Export WebSocket services for game integration
export { webSocketService, gameWebSocketService };
@@ -1,422 +0,0 @@
import fs from 'fs';
import path from 'path';
import { Request, Response, NextFunction } from 'express';
import * as Minio from 'minio';
export enum LogLevel {
REQUEST = 'REQUEST',
ERROR = 'ERROR',
WARNING = 'WARNING',
AUTH = 'AUTH',
DATABASE = 'DATABASE',
STARTUP = 'STARTUP',
CONNECTION = 'CONNECTION',
OTHER = 'OTHER'
}
export interface LogEntry {
timestamp: string;
level: LogLevel;
message: string;
metadata?: any;
requestId?: string;
userId?: string;
ip?: string;
userAgent?: string;
method?: string;
url?: string;
statusCode?: number;
responseTime?: number;
}
export class LoggingService {
private static instance: LoggingService;
private minioClient: Minio.Client | null = null;
private logBuffer: LogEntry[] = [];
private currentLogFile: string | null = null;
private logCount = 0;
private readonly maxLogsPerFile = parseInt(process.env.MAX_LOGS_PER_FILE || '10000');
private readonly logsDir = path.join(process.cwd(), 'logs');
private readonly bucketName = process.env.MINIO_BUCKET_NAME || 'serpentrace-logs';
private uploadInterval: NodeJS.Timeout | null = null;
private constructor() {
this.initializeLogsDirectory();
this.initializeMinioClient();
this.createNewLogFile();
if (process.env.NODE_ENV !== 'test') {
this.startPeriodicUpload();
}
process.on('SIGTERM', () => this.shutdown());
process.on('SIGINT', () => this.shutdown());
process.on('beforeExit', () => this.shutdown());
}
static getInstance(): LoggingService {
if (!LoggingService.instance) {
LoggingService.instance = new LoggingService();
}
return LoggingService.instance;
}
private initializeLogsDirectory(): void {
try {
if (!fs.existsSync(this.logsDir)) {
fs.mkdirSync(this.logsDir, { recursive: true });
}
// Create monthly subdirectory
const monthlyDir = this.getMonthlyDirectory();
if (!fs.existsSync(monthlyDir)) {
fs.mkdirSync(monthlyDir, { recursive: true });
}
} catch (error) {
console.error('Failed to initialize logs directory:', error);
}
}
private initializeMinioClient(): void {
try {
// Check if in production or development
if (process.env.NODE_ENV === 'production') {
if (process.env.MINIO_ENDPOINT && process.env.MINIO_ACCESS_KEY && process.env.MINIO_SECRET_KEY) {
this.minioClient = new Minio.Client({
endPoint: process.env.MINIO_ENDPOINT,
port: parseInt(process.env.MINIO_PORT || '9000'),
useSSL: process.env.MINIO_USE_SSL === 'true',
accessKey: process.env.MINIO_ACCESS_KEY,
secretKey: process.env.MINIO_SECRET_KEY
});
this.ensureBucketExists().catch(error => {
console.warn('MinIO bucket initialization failed:', error.message);
});
} else {
console.warn('Minio configuration not found. Logs will only be stored locally and in console.');
}
} else {
// Development mode - only use MinIO if explicitly configured
if (process.env.MINIO_ENDPOINT || process.env.ENABLE_MINIO === 'true') {
this.minioClient = new Minio.Client({
endPoint: process.env.MINIO_ENDPOINT || 'localhost',
port: parseInt(process.env.MINIO_PORT || '9000'),
useSSL: false,
accessKey: process.env.MINIO_ACCESS_KEY || 'serpentrace',
secretKey: process.env.MINIO_SECRET_KEY || 'serpentrace123!'
});
this.ensureBucketExists().catch(error => {
console.warn('MinIO bucket initialization failed:', error.message);
});
} else {
console.log('Development mode: MinIO disabled. Set ENABLE_MINIO=true to enable MinIO logging.');
this.minioClient = null;
}
}
} catch (error) {
console.error('Failed to initialize Minio client:', error);
this.minioClient = null;
}
}
private async ensureBucketExists(): Promise<void> {
if (!this.minioClient) return;
try {
const exists = await this.minioClient.bucketExists(this.bucketName);
if (!exists) {
await this.minioClient.makeBucket(this.bucketName);
this.log(LogLevel.STARTUP, `Created Minio bucket: ${this.bucketName}`);
}
} catch (error) {
console.warn('MinIO connection failed - disabling MinIO logging:', (error as Error).message);
// Disable MinIO client if connection fails
this.minioClient = null;
}
}
private startPeriodicUpload(): void {
// Upload current log file to Minio every 2 minutes
this.uploadInterval = setInterval(async () => {
if (this.currentLogFile && this.minioClient) {
await this.uploadToMinio(this.currentLogFile);
}
}, 2 * 60 * 1000); // 2 minutes
}
private getMonthlyDirectory(): string {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
return path.join(this.logsDir, `${year}-${month}`);
}
private getMonthlyMinioPrefix(): string {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
return `${year}-${month}/`;
}
private createNewLogFile(): void {
const now = new Date();
const timestamp = now.toISOString().replace(/[:.]/g, '-');
const fileName = `serpentrace-${timestamp}.log`;
this.currentLogFile = path.join(this.getMonthlyDirectory(), fileName);
this.logCount = 0;
// Write log file header
const header = `# SerpentRace Backend Logs\n# Started: ${now.toISOString()}\n# Max entries per file: ${this.maxLogsPerFile}\n\n`;
try {
fs.writeFileSync(this.currentLogFile, header);
} catch (error) {
console.error('Failed to create log file:', error);
}
}
private formatLogEntry(entry: LogEntry): string {
const parts = [
entry.timestamp,
`[${entry.level}]`,
entry.message
];
if (entry.requestId) parts.push(`ReqId:${entry.requestId}`);
if (entry.userId) parts.push(`UserId:${entry.userId}`);
if (entry.ip) parts.push(`IP:${entry.ip}`);
if (entry.method && entry.url) parts.push(`${entry.method} ${entry.url}`);
if (entry.statusCode) parts.push(`Status:${entry.statusCode}`);
if (entry.responseTime) parts.push(`Time:${entry.responseTime}ms`);
if (entry.userAgent) parts.push(`UA:${entry.userAgent.substring(0, 50)}`);
if (entry.metadata) parts.push(`Meta:${JSON.stringify(entry.metadata)}`);
return parts.join(' | ');
}
private async writeToLocalFile(entry: LogEntry): Promise<void> {
if (!this.currentLogFile) return;
try {
const logLine = this.formatLogEntry(entry) + '\n';
fs.appendFileSync(this.currentLogFile, logLine);
this.logCount++;
// Check if we need to rotate the log file
if (this.logCount >= this.maxLogsPerFile) {
await this.rotateLogFile();
}
} catch (error) {
console.error('Failed to write to log file:', error);
}
}
private async rotateLogFile(): Promise<void> {
if (!this.currentLogFile) return;
try {
// Upload current file to Minio before rotating
await this.uploadToMinio(this.currentLogFile);
// Create new log file
this.createNewLogFile();
this.log(LogLevel.OTHER, 'Log file rotated due to size limit');
} catch (error) {
console.error('Failed to rotate log file:', error);
}
}
private async uploadToMinio(filePath: string): Promise<void> {
if (!this.minioClient) {
console.warn('Minio client not initialized, skipping upload');
return;
}
if (!fs.existsSync(filePath)) {
console.warn(`Log file does not exist: ${filePath}`);
return;
}
try {
const fileName = path.basename(filePath);
const objectName = this.getMonthlyMinioPrefix() + fileName;
console.log(`Attempting to upload log file to Minio: ${objectName}`);
await this.minioClient.fPutObject(this.bucketName, objectName, filePath);
console.log(`Successfully uploaded log file to Minio: ${objectName}`);
} catch (error) {
console.error('Failed to upload to Minio:', error);
console.error('Minio config:', {
endpoint: this.minioClient ? 'configured' : 'not configured',
bucket: this.bucketName
});
}
}
private logToConsole(entry: LogEntry): void {
// In production, skip OTHER, CONNECTION, and REQUEST logs
if (process.env.NODE_ENV === 'production') {
if (entry.level === LogLevel.OTHER ||
entry.level === LogLevel.REQUEST) {
return;
}
}
const formattedEntry = this.formatLogEntry(entry);
switch (entry.level) {
case LogLevel.ERROR:
console.error(formattedEntry);
break;
case LogLevel.WARNING:
console.warn(formattedEntry);
break;
case LogLevel.REQUEST:
case LogLevel.AUTH:
case LogLevel.DATABASE:
case LogLevel.CONNECTION:
console.info(formattedEntry);
break;
case LogLevel.STARTUP:
console.log(formattedEntry);
break;
default:
console.log(formattedEntry);
}
}
public log(
level: LogLevel,
message: string,
metadata?: any,
req?: Request,
res?: Response,
responseTime?: number
): void {
// In production, skip OTHER, CONNECTION, and REQUEST logs entirely
if (process.env.NODE_ENV === 'production') {
if (level === LogLevel.OTHER ||
level === LogLevel.CONNECTION ||
level === LogLevel.REQUEST) {
return;
}
}
const entry: LogEntry = {
timestamp: new Date().toISOString(),
level,
message,
metadata
};
// Add request context if available
if (req) {
entry.requestId = (req as any).requestId || this.generateRequestId();
entry.userId = (req as any).user?.userId;
entry.ip = req.ip || req.socket?.remoteAddress || 'unknown';
entry.userAgent = req.get ? req.get('User-Agent') : 'unknown';
entry.method = req.method;
entry.url = req.originalUrl || req.url;
}
if (res) {
entry.statusCode = res.statusCode;
}
if (responseTime !== undefined) {
entry.responseTime = responseTime;
}
// Log to all three destinations
this.logToConsole(entry);
this.writeToLocalFile(entry);
// Add to buffer for potential batch processing
this.logBuffer.push(entry);
// Limit buffer size
if (this.logBuffer.length > 1000) {
this.logBuffer = this.logBuffer.slice(-500);
}
}
private generateRequestId(): string {
return Math.random().toString(36).substr(2, 9);
}
public async shutdown(): Promise<void> {
try {
// Clear the upload interval
if (this.uploadInterval) {
clearInterval(this.uploadInterval);
this.uploadInterval = null;
}
// Upload current log file to Minio
if (this.currentLogFile) {
await this.uploadToMinio(this.currentLogFile);
}
this.log(LogLevel.STARTUP, 'Logging service shutting down gracefully');
// Give time for final logs to be written
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.error('Error during logging service shutdown:', error);
}
}
// Middleware factory methods
public requestLoggingMiddleware() {
return (req: Request, res: Response, next: NextFunction) => {
const startTime = Date.now();
// Generate request ID
(req as any).requestId = this.generateRequestId();
// Log request start
this.log(LogLevel.REQUEST, `Incoming request`, undefined, req);
// Override res.end to log response
const originalEnd = res.end.bind(res);
res.end = (...args: any[]): Response => {
const responseTime = Date.now() - startTime;
LoggingService.getInstance().log(
LogLevel.REQUEST,
`Request completed`,
undefined,
req,
res,
responseTime
);
return originalEnd(...args);
};
next();
};
}
public errorLoggingMiddleware() {
return (error: Error, req: Request, res: Response, next: NextFunction) => {
this.log(
LogLevel.ERROR,
`Unhandled error: ${error.message}`,
{
stack: error.stack,
name: error.name
},
req,
res
);
next(error);
};
}
}
export default LoggingService;
File diff suppressed because it is too large Load Diff
-120
View File
@@ -1,120 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "libReplacement": true, /* Enable lib replacement. */
"experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
"emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
"sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}