diff --git a/apps/api/scripts/code-generation/mongoose/repositories/generate-repository.ts b/apps/api/scripts/code-generation/mongoose/repositories/generate-repository.ts index 8f78ddd..e798c56 100644 --- a/apps/api/scripts/code-generation/mongoose/repositories/generate-repository.ts +++ b/apps/api/scripts/code-generation/mongoose/repositories/generate-repository.ts @@ -1,5 +1,5 @@ -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { NamingUtil } from '../../utils/naming.util'; interface GeneratorConfig { @@ -62,12 +62,18 @@ export class RepositoryGenerator { const templatePath = path.join(this.templatesDir, templateName); let template = fs.readFileSync(templatePath, 'utf-8'); - template = template.replace( - /{{ENTITY_NAME_PASCAL}}/g, + template = template.replaceAll( + '{{ENTITY_NAME_PASCAL}}', this.entityNamePascal, ); - template = template.replace(/{{ENTITY_NAME_KEBAB}}/g, this.entityNameKebab); - template = template.replace(/{{ENTITY_NAME_CAMEL}}/g, this.entityNameCamel); + template = template.replaceAll( + '{{ENTITY_NAME_KEBAB}}', + this.entityNameKebab, + ); + template = template.replaceAll( + '{{ENTITY_NAME_CAMEL}}', + this.entityNameCamel, + ); return template; } diff --git a/apps/api/scripts/code-generation/prisma/repositories/generate-repository.ts b/apps/api/scripts/code-generation/prisma/repositories/generate-repository.ts index f091759..4c9800c 100644 --- a/apps/api/scripts/code-generation/prisma/repositories/generate-repository.ts +++ b/apps/api/scripts/code-generation/prisma/repositories/generate-repository.ts @@ -1,5 +1,5 @@ -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; import { NamingUtil } from '../../utils/naming.util'; interface GeneratorConfig { @@ -62,12 +62,18 @@ export class RepositoryGenerator { const templatePath = path.join(this.templatesDir, templateName); let template = fs.readFileSync(templatePath, 'utf-8'); - template = template.replace( - /{{ENTITY_NAME_PASCAL}}/g, + template = template.replaceAll( + '{{ENTITY_NAME_PASCAL}}', this.entityNamePascal, ); - template = template.replace(/{{ENTITY_NAME_KEBAB}}/g, this.entityNameKebab); - template = template.replace(/{{ENTITY_NAME_CAMEL}}/g, this.entityNameCamel); + template = template.replaceAll( + '{{ENTITY_NAME_KEBAB}}', + this.entityNameKebab, + ); + template = template.replaceAll( + '{{ENTITY_NAME_CAMEL}}', + this.entityNameCamel, + ); return template; } diff --git a/apps/api/scripts/code-generation/utils/naming.util.ts b/apps/api/scripts/code-generation/utils/naming.util.ts index 500a2c8..86b50f7 100644 --- a/apps/api/scripts/code-generation/utils/naming.util.ts +++ b/apps/api/scripts/code-generation/utils/naming.util.ts @@ -2,8 +2,8 @@ export class NamingUtil { static toKebabCase(str: string): string { return str .trim() - .replace(/([a-z])([A-Z])/g, '$1-$2') - .replace(/[\s_]+/g, '-') + .replaceAll(/([a-z])([A-Z])/g, '$1-$2') + .replaceAll(/[\s_]+/g, '-') .toLowerCase(); } @@ -11,7 +11,7 @@ export class NamingUtil { return str .trim() .split(/[-_\s]+/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) .join(''); } diff --git a/apps/api/src/decorators/class/auto-transaction.decorator.ts b/apps/api/src/decorators/class/auto-transaction.decorator.ts index f2ab010..de202c1 100644 --- a/apps/api/src/decorators/class/auto-transaction.decorator.ts +++ b/apps/api/src/decorators/class/auto-transaction.decorator.ts @@ -5,53 +5,12 @@ import { type TransactionalAdapter, } from '@nestjs-cls/transactional'; import { NO_TRANSACTION_KEY } from '../constants'; -import { mkdirSync, writeFileSync } from 'fs'; -import { join } from 'path'; type TOptionsFromAdapter = TAdapter extends TransactionalAdapter ? TOptions : never; -class TransactionalLogger { - private readonly processDir: string; - - constructor() { - const baseLogDir = join(process.cwd(), 'tmp', 'transaction'); - - const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); - const random = Math.random().toString(36).substring(2, 8); - const processId = `${timestamp}_${random}`; - - this.processDir = join(baseLogDir, processId); - mkdirSync(this.processDir, { recursive: true }); - - this.log('Session', 'Transaction validation session started'); - this.log('Session', `Process ID: ${processId}`); - this.log('Session', `Log directory: ${this.processDir}`); - } - - log(className: string, message: string): void { - const timestamp = new Date().toISOString(); - const logMessage = `[${timestamp}] ${message}`; - - const classLogFile = join(this.processDir, `${className}.log`); - - writeFileSync(classLogFile, logMessage + '\n', { - flag: 'a', - encoding: 'utf-8', - }); - - // console.log(`[${timestamp}] [${className}] ${message}`); - } - - getProcessDir(): string { - return this.processDir; - } -} - -const logger = new TransactionalLogger(); - /** * Run the decorated class methods in a transaction. * @@ -120,7 +79,7 @@ export function AutoTransaction( ): ClassDecorator { return (target) => { if (typeof target !== 'function') { - throw new Error( + throw new TypeError( `@AutoTransaction can only be used on classes, but the target is not a function.`, ); } @@ -140,10 +99,6 @@ export function AutoTransaction( ); } - const methodsProcessed: string[] = []; - const methodsSkipped: string[] = []; - const methodsWithNoTransaction: string[] = []; - for (const name of Object.getOwnPropertyNames(proto)) { if (name === 'constructor') { continue; @@ -151,12 +106,10 @@ export function AutoTransaction( const descriptor = Object.getOwnPropertyDescriptor(proto, name); if (!descriptor) { - methodsSkipped.push(`${name} (no descriptor)`); continue; } if (typeof descriptor.value !== 'function') { - methodsSkipped.push(`${name} (not a function)`); continue; } @@ -166,7 +119,6 @@ export function AutoTransaction( ); if (noTransaction) { - methodsWithNoTransaction.push(name); continue; } @@ -186,39 +138,7 @@ export function AutoTransaction( ); } - logger.log(className, `✓ ${name}: Function wrapped in transaction proxy`); - Object.defineProperty(proto, name, descriptor); - - methodsProcessed.push(name); } - - logger.log(className, `@AutoTransaction decorator applied`); - - logger.log(className, `Summary:`); - logger.log( - className, - ` - Methods wrapped: ${methodsProcessed.length} (${methodsProcessed.join(', ') || 'none'})`, - ); - logger.log( - className, - ` - Methods with @NoTransaction: ${methodsWithNoTransaction.length} (${methodsWithNoTransaction.join(', ') || 'none'})`, - ); - logger.log( - className, - ` - Methods skipped: ${methodsSkipped.length} (${methodsSkipped.join(', ') || 'none'})`, - ); - - if ( - methodsProcessed.length === 0 && - methodsWithNoTransaction.length === 0 - ) { - logger.log( - className, - `⚠ Warning: No methods were wrapped. This may indicate the decorator is applied to a class with no methods.`, - ); - } - - logger.log(className, `Process directory: ${logger.getProcessDir()}`); }; } diff --git a/apps/api/src/modules/auth/auth.service.ts b/apps/api/src/modules/auth/auth.service.ts index 20a1ec7..09b50a5 100644 --- a/apps/api/src/modules/auth/auth.service.ts +++ b/apps/api/src/modules/auth/auth.service.ts @@ -87,8 +87,7 @@ export class AuthService { }); if ( - !token || - !token.accessToken || + !token?.accessToken || !token.accessTokenExpiresAt || DateExtensions.hasDatePassed(token.accessTokenExpiresAt) ) { @@ -121,7 +120,7 @@ export class AuthService { }, }); - if (!tokens || !tokens.accessToken) { + if (!tokens?.accessToken) { throw new Error( `Failed to refresh token for user ${account.userId} and provider ${provider}. User may need to re-authenticate.`, ); diff --git a/apps/api/src/modules/crud/schemas/crud.schema.ts b/apps/api/src/modules/crud/schemas/crud.schema.ts index b5a7ab0..35f3521 100644 --- a/apps/api/src/modules/crud/schemas/crud.schema.ts +++ b/apps/api/src/modules/crud/schemas/crud.schema.ts @@ -17,7 +17,9 @@ export const ZUpdateCrudDto = ZCrud.pick({ content: true, }); -export const ZCrudCreateRequest = ZBaseRequest.merge(ZCreateCrudDto); +export const ZCrudCreateRequest = ZBaseRequest.extend({ + content: z.string().min(1).max(1000), +}); export const ZCrudCreateResponse = ZBaseResponse.extend({ id: z.string(), diff --git a/apps/api/src/repositories/mongoose/mongoose.base-repository.ts b/apps/api/src/repositories/mongoose/mongoose.base-repository.ts index 119cbb3..8ee02c7 100644 --- a/apps/api/src/repositories/mongoose/mongoose.base-repository.ts +++ b/apps/api/src/repositories/mongoose/mongoose.base-repository.ts @@ -15,8 +15,7 @@ import { MongooseBaseEntity } from './mongoose.base-entity'; export abstract class MongooseBaseRepository< TDomainEntity extends BaseEntity, TDbEntity extends MongooseBaseEntity, -> implements IMongooseRepository -{ +> implements IMongooseRepository { protected readonly model: Model; protected readonly mongoTxHost: TransactionHost; diff --git a/apps/api/src/schemas/base.schema.ts b/apps/api/src/schemas/base.schema.ts index 17d7797..c9d17a9 100644 --- a/apps/api/src/schemas/base.schema.ts +++ b/apps/api/src/schemas/base.schema.ts @@ -1,10 +1,6 @@ import { z } from 'zod'; -export const ZBaseRequest = z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), -}); - +export const ZBaseRequest = z.object({}); export const ZBaseResponse = z.object({ success: z.boolean(), message: z.string().optional(), diff --git a/apps/mobile/app/crud.tsx b/apps/mobile/app/crud.tsx index a313a91..f9f70a8 100644 --- a/apps/mobile/app/crud.tsx +++ b/apps/mobile/app/crud.tsx @@ -257,7 +257,7 @@ const styles = StyleSheet.create({ }, }); -function CrudPanel({ dbType }: { dbType: DbType }) { +function CrudPanel({ dbType }: Readonly<{ dbType: DbType }>) { const utils = trpc.useUtils(); const [content, setContent] = useState(""); const [editingId, setEditingId] = useState(null); diff --git a/apps/web/app/crud-demo/page.tsx b/apps/web/app/crud-demo/page.tsx index c1cfa05..0ecf9d4 100644 --- a/apps/web/app/crud-demo/page.tsx +++ b/apps/web/app/crud-demo/page.tsx @@ -11,7 +11,7 @@ interface CrudItem { content: string; } -function CrudPanel({ dbType }: { dbType: DbType }) { +function CrudPanel({ dbType }: Readonly<{ dbType: DbType }>) { const utils = trpc.useUtils(); const [content, setContent] = useState(""); const [editingId, setEditingId] = useState(null); diff --git a/packages/sonarqube/sonar-project.properties b/packages/sonarqube/sonar-project.properties index cc8ce8d..60a5242 100644 --- a/packages/sonarqube/sonar-project.properties +++ b/packages/sonarqube/sonar-project.properties @@ -35,7 +35,8 @@ sonar.exclusions=\ **/vendor/**,\ **/tmp/**,\ **/temp/**,\ - **/.cache/** + **/.cache/**,\ + packages/trpc/src/server/server.ts # Exclude specific directories per package # sonar.exclusions=apps/web/dist/**,apps/api/dist/**,packages/*/dist/** diff --git a/packages/trpc/src/server/server.ts b/packages/trpc/src/server/server.ts index 77d4966..388f2a6 100644 --- a/packages/trpc/src/server/server.ts +++ b/packages/trpc/src/server/server.ts @@ -6,27 +6,15 @@ const publicProcedure = t.procedure; const appRouter = t.router({ crud: t.router({ - createCrudMongo: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).merge(z.object({ - id: z.string(), - createdAt: z.date(), - updatedAt: z.date(), - }).extend({ + createCrudMongo: publicProcedure.input(z.object({}).extend({ content: z.string().min(1).max(1000), - }).pick({ - content: true, - }))).output(z.object({ + })).output(z.object({ success: z.boolean(), message: z.string().optional(), }).extend({ id: z.string(), })).mutation(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - findAllMongo: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).extend({ + findAllMongo: publicProcedure.input(z.object({}).extend({ limit: z.number().int().positive().max(100).default(10).optional(), offset: z.number().int().nonnegative().default(0).optional(), })).output(z.object({ @@ -44,10 +32,7 @@ const appRouter = t.router({ limit: z.number().int().positive(), offset: z.number().int().nonnegative(), })).query(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - findOneCrudMongo: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).extend({ + findOneCrudMongo: publicProcedure.input(z.object({}).extend({ id: z.string(), })).output(z.object({ id: z.string(), @@ -56,10 +41,7 @@ const appRouter = t.router({ }).extend({ content: z.string().min(1).max(1000), }).nullable()).query(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - updateCrudMongo: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).extend({ + updateCrudMongo: publicProcedure.input(z.object({}).extend({ id: z.string(), data: z.object({ id: z.string(), @@ -84,36 +66,21 @@ const appRouter = t.router({ content: z.string().min(1).max(1000), }).optional(), })).mutation(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - deleteCrudMongo: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).extend({ + deleteCrudMongo: publicProcedure.input(z.object({}).extend({ id: z.string(), })).output(z.object({ success: z.boolean(), message: z.string().optional(), })).mutation(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - createCrudPrisma: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).merge(z.object({ - id: z.string(), - createdAt: z.date(), - updatedAt: z.date(), - }).extend({ + createCrudPrisma: publicProcedure.input(z.object({}).extend({ content: z.string().min(1).max(1000), - }).pick({ - content: true, - }))).output(z.object({ + })).output(z.object({ success: z.boolean(), message: z.string().optional(), }).extend({ id: z.string(), })).mutation(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - findAllPrisma: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).extend({ + findAllPrisma: publicProcedure.input(z.object({}).extend({ limit: z.number().int().positive().max(100).default(10).optional(), offset: z.number().int().nonnegative().default(0).optional(), })).output(z.object({ @@ -131,10 +98,7 @@ const appRouter = t.router({ limit: z.number().int().positive(), offset: z.number().int().nonnegative(), })).query(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - findOneCrudPrisma: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).extend({ + findOneCrudPrisma: publicProcedure.input(z.object({}).extend({ id: z.string(), })).output(z.object({ id: z.string(), @@ -143,10 +107,7 @@ const appRouter = t.router({ }).extend({ content: z.string().min(1).max(1000), }).nullable()).query(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - updateCrudPrisma: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).extend({ + updateCrudPrisma: publicProcedure.input(z.object({}).extend({ id: z.string(), data: z.object({ id: z.string(), @@ -171,10 +132,7 @@ const appRouter = t.router({ content: z.string().min(1).max(1000), }).optional(), })).mutation(async () => "PLACEHOLDER_DO_NOT_REMOVE" as any), - deleteCrudPrisma: publicProcedure.input(z.object({ - requestId: z.string().uuid().optional(), - timestamp: z.number().optional(), - }).extend({ + deleteCrudPrisma: publicProcedure.input(z.object({}).extend({ id: z.string(), })).output(z.object({ success: z.boolean(), diff --git a/packages/utils/core/src/logger/log-level.ts b/packages/utils/core/src/logger/log-level.ts index 97d992b..7e11731 100644 --- a/packages/utils/core/src/logger/log-level.ts +++ b/packages/utils/core/src/logger/log-level.ts @@ -1,10 +1,10 @@ export enum LogLevel { None = 0, - Info = 1 << 0, - Warn = 1 << 1, - Debug = 1 << 2, - Error = 1 << 3, - Trace = 1 << 4, - Critical = 1 << 5, - All = Info | Warn | Debug | Error | Trace | Critical, + Info = 1, + Warn = 2, + Debug = 4, + Error = 8, + Trace = 16, + Critical = 32, + All = 63, } diff --git a/packages/utils/core/src/logger/noop-logger.ts b/packages/utils/core/src/logger/noop-logger.ts index b2059d7..f79266a 100644 --- a/packages/utils/core/src/logger/noop-logger.ts +++ b/packages/utils/core/src/logger/noop-logger.ts @@ -4,40 +4,54 @@ import { BaseLogger } from "./base-logger"; import { LogLevel } from "./log-level"; export class NoopLogger extends BaseLogger { - static create(logLevel: LogLevel): NoopLogger { - return new NoopLogger(logLevel); - } - public constructor(logLevel: LogLevel) { super(logLevel); } + static create(logLevel: LogLevel): NoopLogger { + return new NoopLogger(logLevel); + } + log(_: LogLevel, __: any): void; log(_: LogLevel, __: any, ...___: any[]): void; - log(_: LogLevel, __: any, ...___: any[]): void {} + log(_: LogLevel, __: any, ...___: any[]): void { + // Intentionally empty - NoopLogger implements the Null Object pattern to disable logging without code changes + } info(_: any): void; info(_: any, ...__: any[]): void; - info(_: any, ...__: any[]): void {} + info(_: any, ...__: any[]): void { + // Intentionally empty - NoopLogger implements the Null Object pattern to disable logging without code changes + } warn(_: any): void; warn(_: any, ...__: any[]): void; - warn(_: any, ...__: any[]): void {} + warn(_: any, ...__: any[]): void { + // Intentionally empty - NoopLogger implements the Null Object pattern to disable logging without code changes + } debug(_: any): void; debug(_: any, ...__: any[]): void; - debug(_: any, ...__: any[]): void {} + debug(_: any, ...__: any[]): void { + // Intentionally empty - NoopLogger implements the Null Object pattern to disable logging without code changes + } trace(_: any): void; trace(_: any, __: string, ...___: any[]): void; trace(_: any, ...__: any[]): void; - trace(_: any, ...__: any[]): void {} + trace(_: any, ...__: any[]): void { + // Intentionally empty - NoopLogger implements the Null Object pattern to disable logging without code changes + } error(_: any): void; error(_: any, ...__: any[]): void; - error(_: any, ...__: any[]): void {} + error(_: any, ...__: any[]): void { + // Intentionally empty - NoopLogger implements the Null Object pattern to disable logging without code changes + } critical(_: any): void; critical(_: any, ...__: any[]): void; - critical(_: unknown, ...__: unknown[]): void {} + critical(_: unknown, ...__: unknown[]): void { + // Intentionally empty - NoopLogger implements the Null Object pattern to disable logging without code changes + } } diff --git a/packages/utils/core/tsconfig.json b/packages/utils/core/tsconfig.json index eac161d..7a9de74 100644 --- a/packages/utils/core/tsconfig.json +++ b/packages/utils/core/tsconfig.json @@ -8,7 +8,8 @@ "esModuleInterop": true, "skipLibCheck": true, "experimentalDecorators": true, - "emitDecoratorMetadata": true + "emitDecoratorMetadata": true, + "lib": ["es2022"] }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"]