[{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/app.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/auth-admin.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/auth.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/auth.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/auth.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/auth.types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/dto/link-whmcs.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/dto/login.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/dto/request-password-reset.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/dto/reset-password.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/dto/set-password.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/dto/signup.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/guards/admin.guard.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/guards/auth-throttle.guard.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/guards/jwt-auth.guard.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/guards/local-auth.guard.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/services/token-blacklist.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/strategies/jwt.strategy.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/auth/strategies/local.strategy.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/cases/cases.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/cases/cases.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/cases/cases.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/catalog/catalog.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/catalog/catalog.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/catalog/catalog.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/audit/audit.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/audit/audit.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/cache/cache.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/cache/cache.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/config/env.validation.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/email/email.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/email/email.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/email/providers/sendgrid.provider.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/email/queue/email.processor.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/email/queue/email.queue.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/filters/http-exception.filter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/logging/logging.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/logging/logging.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/prisma/prisma.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/prisma/prisma.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/redis/redis.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/common/utils/error.util.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/health/health.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/health/health.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/invoices/invoices.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/invoices/invoices.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/invoices/invoices.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/jobs/jobs.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/jobs/jobs.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/jobs/reconcile.processor.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/main.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/mappings/cache/mapping-cache.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/mappings/mappings.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/mappings/mappings.service.ts","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":2,"message":"Unsafe assignment of an `any` value.","line":576,"column":13,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":576,"endColumn":59}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import {\n Injectable,\n NotFoundException,\n ConflictException,\n BadRequestException,\n Inject,\n} from \"@nestjs/common\";\nimport { Logger } from \"nestjs-pino\";\nimport { PrismaService } from \"../common/prisma/prisma.service\";\nimport { getErrorMessage } from \"../common/utils/error.util\";\nimport { MappingCacheService } from \"./cache/mapping-cache.service\";\nimport { MappingValidatorService } from \"./validation/mapping-validator.service\";\nimport {\n UserIdMapping,\n CreateMappingRequest,\n UpdateMappingRequest,\n MappingSearchFilters,\n MappingStats,\n BulkMappingResult,\n} from \"./types/mapping.types\";\n\n@Injectable()\nexport class MappingsService {\n constructor(\n private readonly prisma: PrismaService,\n private readonly cacheService: MappingCacheService,\n private readonly validator: MappingValidatorService,\n @Inject(Logger) private readonly logger: Logger\n ) {}\n\n /**\n * Create a new user mapping\n */\n async createMapping(request: CreateMappingRequest): Promise {\n try {\n // Validate request\n const validation = this.validator.validateCreateRequest(request);\n this.validator.logValidationResult(\"Create mapping\", validation, {\n userId: request.userId,\n });\n\n if (!validation.isValid) {\n throw new BadRequestException(`Invalid mapping data: ${validation.errors.join(\", \")}`);\n }\n\n // Sanitize input\n const sanitizedRequest = this.validator.sanitizeCreateRequest(request);\n\n // Check for conflicts\n const existingMappings = await this.getAllMappingsFromDb();\n const conflictValidation = this.validator.validateNoConflicts(\n sanitizedRequest,\n existingMappings\n );\n\n if (!conflictValidation.isValid) {\n throw new ConflictException(`Mapping conflict: ${conflictValidation.errors.join(\", \")}`);\n }\n\n // Create in database\n const created = await this.prisma.idMapping.create({\n data: sanitizedRequest,\n });\n\n const mapping: UserIdMapping = {\n userId: created.userId,\n whmcsClientId: created.whmcsClientId,\n sfAccountId: created.sfAccountId || undefined,\n createdAt: created.createdAt,\n updatedAt: created.updatedAt,\n };\n\n // Cache the new mapping\n await this.cacheService.setMapping(mapping);\n\n this.logger.log(`Created mapping for user ${mapping.userId}`, {\n whmcsClientId: mapping.whmcsClientId,\n sfAccountId: mapping.sfAccountId,\n warnings: validation.warnings,\n });\n\n return mapping;\n } catch (error) {\n this.logger.error(`Failed to create mapping for user ${request.userId}`, {\n error: getErrorMessage(error),\n request: this.sanitizeForLog(request),\n });\n throw error;\n }\n }\n\n /**\n * Find mapping by user ID\n */\n async findByUserId(userId: string): Promise {\n try {\n // Validate user ID\n if (!userId) {\n throw new BadRequestException(\"User ID is required\");\n }\n\n // Try cache first\n const cached = await this.cacheService.getByUserId(userId);\n if (cached) {\n this.logger.debug(`Cache hit for user mapping: ${userId}`);\n return cached;\n }\n\n // Fetch from database\n const dbMapping = await this.prisma.idMapping.findUnique({\n where: { userId },\n });\n\n if (!dbMapping) {\n this.logger.debug(`No mapping found for user ${userId}`);\n return null;\n }\n\n const mapping: UserIdMapping = {\n userId: dbMapping.userId,\n whmcsClientId: dbMapping.whmcsClientId,\n sfAccountId: dbMapping.sfAccountId || undefined,\n createdAt: dbMapping.createdAt,\n updatedAt: dbMapping.updatedAt,\n };\n\n // Cache the result\n await this.cacheService.setMapping(mapping);\n\n this.logger.debug(`Found mapping for user ${userId}`, {\n whmcsClientId: mapping.whmcsClientId,\n sfAccountId: mapping.sfAccountId,\n });\n\n return mapping;\n } catch (error) {\n this.logger.error(`Failed to find mapping for user ${userId}`, {\n error: getErrorMessage(error),\n });\n throw error;\n }\n }\n\n /**\n * Find mapping by WHMCS client ID\n */\n async findByWhmcsClientId(whmcsClientId: number): Promise {\n try {\n // Validate WHMCS client ID\n if (!whmcsClientId || whmcsClientId < 1) {\n throw new BadRequestException(\"Valid WHMCS client ID is required\");\n }\n\n // Try cache first\n const cached = await this.cacheService.getByWhmcsClientId(whmcsClientId);\n if (cached) {\n this.logger.debug(`Cache hit for WHMCS client mapping: ${whmcsClientId}`);\n return cached;\n }\n\n // Fetch from database\n const dbMapping = await this.prisma.idMapping.findUnique({\n where: { whmcsClientId },\n });\n\n if (!dbMapping) {\n this.logger.debug(`No mapping found for WHMCS client ${whmcsClientId}`);\n return null;\n }\n\n const mapping: UserIdMapping = {\n userId: dbMapping.userId,\n whmcsClientId: dbMapping.whmcsClientId,\n sfAccountId: dbMapping.sfAccountId || undefined,\n createdAt: dbMapping.createdAt,\n updatedAt: dbMapping.updatedAt,\n };\n\n // Cache the result\n await this.cacheService.setMapping(mapping);\n\n this.logger.debug(`Found mapping for WHMCS client ${whmcsClientId}`, {\n userId: mapping.userId,\n sfAccountId: mapping.sfAccountId,\n });\n\n return mapping;\n } catch (error) {\n this.logger.error(`Failed to find mapping for WHMCS client ${whmcsClientId}`, {\n error: getErrorMessage(error),\n });\n throw error;\n }\n }\n\n /**\n * Find mapping by Salesforce account ID\n */\n async findBySfAccountId(sfAccountId: string): Promise {\n try {\n // Validate Salesforce account ID\n if (!sfAccountId) {\n throw new BadRequestException(\"Salesforce account ID is required\");\n }\n\n // Try cache first\n const cached = await this.cacheService.getBySfAccountId(sfAccountId);\n if (cached) {\n this.logger.debug(`Cache hit for SF account mapping: ${sfAccountId}`);\n return cached;\n }\n\n // Fetch from database\n const dbMapping = await this.prisma.idMapping.findFirst({\n where: { sfAccountId },\n });\n\n if (!dbMapping) {\n this.logger.debug(`No mapping found for SF account ${sfAccountId}`);\n return null;\n }\n\n const mapping: UserIdMapping = {\n userId: dbMapping.userId,\n whmcsClientId: dbMapping.whmcsClientId,\n sfAccountId: dbMapping.sfAccountId || undefined,\n createdAt: dbMapping.createdAt,\n updatedAt: dbMapping.updatedAt,\n };\n\n // Cache the result\n await this.cacheService.setMapping(mapping);\n\n this.logger.debug(`Found mapping for SF account ${sfAccountId}`, {\n userId: mapping.userId,\n whmcsClientId: mapping.whmcsClientId,\n });\n\n return mapping;\n } catch (error) {\n this.logger.error(`Failed to find mapping for SF account ${sfAccountId}`, {\n error: getErrorMessage(error),\n });\n throw error;\n }\n }\n\n /**\n * Update an existing mapping\n */\n async updateMapping(userId: string, updates: UpdateMappingRequest): Promise {\n try {\n // Validate request\n const validation = this.validator.validateUpdateRequest(userId, updates);\n this.validator.logValidationResult(\"Update mapping\", validation, {\n userId,\n });\n\n if (!validation.isValid) {\n throw new BadRequestException(`Invalid update data: ${validation.errors.join(\", \")}`);\n }\n\n // Get existing mapping\n const existing = await this.findByUserId(userId);\n if (!existing) {\n throw new NotFoundException(`Mapping not found for user ${userId}`);\n }\n\n // Sanitize input\n const sanitizedUpdates = this.validator.sanitizeUpdateRequest(updates);\n\n // Check for conflicts if WHMCS client ID is being changed\n if (\n sanitizedUpdates.whmcsClientId &&\n sanitizedUpdates.whmcsClientId !== existing.whmcsClientId\n ) {\n const conflictingMapping = await this.findByWhmcsClientId(sanitizedUpdates.whmcsClientId);\n if (conflictingMapping && conflictingMapping.userId !== userId) {\n throw new ConflictException(\n `WHMCS client ${sanitizedUpdates.whmcsClientId} is already mapped to user ${conflictingMapping.userId}`\n );\n }\n }\n\n // Update in database\n const updated = await this.prisma.idMapping.update({\n where: { userId },\n data: sanitizedUpdates,\n });\n\n const newMapping: UserIdMapping = {\n userId: updated.userId,\n whmcsClientId: updated.whmcsClientId,\n sfAccountId: updated.sfAccountId || undefined,\n createdAt: updated.createdAt,\n updatedAt: updated.updatedAt,\n };\n\n // Update cache\n await this.cacheService.updateMapping(existing, newMapping);\n\n this.logger.log(`Updated mapping for user ${userId}`, {\n changes: sanitizedUpdates,\n warnings: validation.warnings,\n });\n\n return newMapping;\n } catch (error) {\n this.logger.error(`Failed to update mapping for user ${userId}`, {\n error: getErrorMessage(error),\n updates: this.sanitizeForLog(updates),\n });\n throw error;\n }\n }\n\n /**\n * Delete a mapping\n */\n async deleteMapping(userId: string): Promise {\n try {\n // Get existing mapping\n const existing = await this.findByUserId(userId);\n if (!existing) {\n throw new NotFoundException(`Mapping not found for user ${userId}`);\n }\n\n // Validate deletion\n const validation = this.validator.validateDeletion(existing);\n this.validator.logValidationResult(\"Delete mapping\", validation, {\n userId,\n });\n\n // Delete from database\n await this.prisma.idMapping.delete({\n where: { userId },\n });\n\n // Remove from cache\n await this.cacheService.deleteMapping(existing);\n\n this.logger.log(`Deleted mapping for user ${userId}`, {\n whmcsClientId: existing.whmcsClientId,\n sfAccountId: existing.sfAccountId,\n warnings: validation.warnings,\n });\n } catch (error) {\n this.logger.error(`Failed to delete mapping for user ${userId}`, {\n error: getErrorMessage(error),\n });\n throw error;\n }\n }\n\n /**\n * Search mappings with filters\n */\n async searchMappings(filters: MappingSearchFilters): Promise {\n try {\n const whereClause: Record = {};\n\n if (filters.userId) {\n whereClause.userId = filters.userId;\n }\n\n if (filters.whmcsClientId) {\n whereClause.whmcsClientId = filters.whmcsClientId;\n }\n\n if (filters.sfAccountId) {\n whereClause.sfAccountId = filters.sfAccountId;\n }\n\n if (filters.hasWhmcsMapping !== undefined) {\n whereClause.whmcsClientId = filters.hasWhmcsMapping ? { not: null } : null;\n }\n\n if (filters.hasSfMapping !== undefined) {\n if (filters.hasSfMapping) {\n whereClause.sfAccountId = { not: null };\n } else {\n whereClause.sfAccountId = null;\n }\n }\n\n const dbMappings = await this.prisma.idMapping.findMany({\n where: whereClause,\n orderBy: { createdAt: \"desc\" },\n });\n\n const mappings: UserIdMapping[] = dbMappings.map(mapping => ({\n userId: mapping.userId,\n whmcsClientId: mapping.whmcsClientId,\n sfAccountId: mapping.sfAccountId || undefined,\n createdAt: mapping.createdAt,\n updatedAt: mapping.updatedAt,\n }));\n\n this.logger.debug(`Found ${mappings.length} mappings matching filters`, filters);\n return mappings;\n } catch (error) {\n this.logger.error(\"Failed to search mappings\", {\n error: getErrorMessage(error),\n filters,\n });\n throw error;\n }\n }\n\n /**\n * Get mapping statistics\n */\n async getMappingStats(): Promise {\n try {\n const [totalCount, whmcsCount, sfCount, completeCount] = await Promise.all([\n this.prisma.idMapping.count(),\n // whmcsClientId is non-nullable; this count equals total mappings\n this.prisma.idMapping.count(),\n this.prisma.idMapping.count({\n where: { sfAccountId: { not: null } },\n }),\n // Complete mappings are those with a non-null sfAccountId (whmcsClientId is always present)\n this.prisma.idMapping.count({\n where: { sfAccountId: { not: null } },\n }),\n ]);\n\n const stats: MappingStats = {\n totalMappings: totalCount,\n whmcsMappings: whmcsCount,\n salesforceMappings: sfCount,\n completeMappings: completeCount,\n orphanedMappings: 0, // Would need to check against actual user records\n };\n\n this.logger.debug(\"Generated mapping statistics\", stats);\n return stats;\n } catch (error) {\n this.logger.error(\"Failed to get mapping statistics\", {\n error: getErrorMessage(error),\n });\n throw error;\n }\n }\n\n /**\n * Bulk create mappings\n */\n async bulkCreateMappings(mappings: CreateMappingRequest[]): Promise {\n const result: BulkMappingResult = {\n successful: 0,\n failed: 0,\n errors: [],\n };\n\n try {\n // Validate all mappings first\n const validations = this.validator.validateBulkMappings(mappings);\n\n for (let i = 0; i < mappings.length; i++) {\n const mapping = mappings[i];\n const validation = validations[i].validation;\n\n try {\n if (!validation.isValid) {\n throw new Error(validation.errors.join(\", \"));\n }\n\n await this.createMapping(mapping);\n result.successful++;\n } catch (error) {\n result.failed++;\n result.errors.push({\n index: i,\n error: getErrorMessage(error),\n data: mapping,\n });\n }\n }\n\n this.logger.log(\n `Bulk create completed: ${result.successful} successful, ${result.failed} failed`\n );\n return result;\n } catch (error) {\n this.logger.error(\"Bulk create mappings failed\", {\n error: getErrorMessage(error),\n });\n throw error;\n }\n }\n\n /**\n * Check if user has mapping\n */\n async hasMapping(userId: string): Promise {\n try {\n // Try cache first\n const cached = await this.cacheService.getByUserId(userId);\n if (cached) {\n return true;\n }\n\n // Check database\n const mapping = await this.prisma.idMapping.findUnique({\n where: { userId },\n select: { userId: true },\n });\n\n return mapping !== null;\n } catch (error) {\n this.logger.error(`Failed to check mapping for user ${userId}`, {\n error: getErrorMessage(error),\n });\n return false;\n }\n }\n\n /**\n * Invalidate cache for a user\n */\n async invalidateCache(userId: string): Promise {\n // Get the current mapping to invalidate all related cache keys\n const mapping = await this.cacheService.getByUserId(userId);\n if (mapping) {\n await this.cacheService.deleteMapping(mapping);\n }\n this.logger.log(`Invalidated mapping cache for user ${userId}`);\n }\n\n /**\n * Health check\n */\n async healthCheck(): Promise<{ status: string; details: Record }> {\n try {\n // Test database connectivity\n await this.prisma.idMapping.count();\n\n return {\n status: \"healthy\",\n details: {\n database: \"connected\",\n cache: \"available\",\n timestamp: new Date().toISOString(),\n },\n };\n } catch (error) {\n this.logger.error(\"Mapping service health check failed\", {\n error: getErrorMessage(error),\n });\n return {\n status: \"unhealthy\",\n details: {\n error: getErrorMessage(error),\n timestamp: new Date().toISOString(),\n },\n };\n }\n }\n\n // Private helper methods\n\n private async getAllMappingsFromDb(): Promise {\n const dbMappings = await this.prisma.idMapping.findMany();\n return dbMappings.map(mapping => ({\n userId: mapping.userId,\n whmcsClientId: mapping.whmcsClientId,\n sfAccountId: mapping.sfAccountId || undefined,\n createdAt: mapping.createdAt,\n updatedAt: mapping.updatedAt,\n }));\n }\n\n private sanitizeForLog(data: unknown): Record {\n try {\n const plain = JSON.parse(JSON.stringify(data ?? {}));\n if (plain && typeof plain === \"object\" && !Array.isArray(plain)) {\n return plain as Record;\n }\n return { value: plain } as Record;\n } catch {\n return { value: String(data) } as Record;\n }\n }\n\n /**\n * Legacy method support (for backward compatibility)\n */\n async create(data: CreateMappingRequest): Promise {\n this.logger.warn(\"Using legacy create method - please update to createMapping\");\n return this.createMapping(data);\n }\n\n /**\n * Legacy method support (for backward compatibility)\n */\n async createMappingLegacy(data: CreateMappingRequest): Promise {\n this.logger.warn(\"Using legacy createMapping method - please update to createMapping\");\n return this.createMapping(data);\n }\n\n /**\n * Legacy method support (for backward compatibility)\n */\n async updateMappingLegacy(userId: string, updates: UpdateMappingRequest): Promise {\n this.logger.warn(\"Using legacy updateMapping method - please update to updateMapping\");\n return this.updateMapping(userId, updates);\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/mappings/types/mapping.types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/mappings/validation/mapping-validator.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/orders/orders.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/orders/orders.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/orders/orders.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/subscriptions/subscriptions.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/subscriptions/subscriptions.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/subscriptions/subscriptions.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/users/dto/update-billing.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/users/dto/update-user.dto.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/users/users.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/users/users.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/users/users.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/salesforce/salesforce.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/salesforce/salesforce.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/salesforce/services/salesforce-account.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/salesforce/services/salesforce-case.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/salesforce/services/salesforce-connection.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/vendors.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/cache/whmcs-cache.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/services/whmcs-client.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/services/whmcs-connection.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/services/whmcs-invoice.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/services/whmcs-payment.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/services/whmcs-sso.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/services/whmcs-subscription.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/transformers/whmcs-data.transformer.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/types/whmcs-api.types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/whmcs.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/vendors/whmcs/whmcs.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/webhooks/guards/webhook-signature.guard.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/webhooks/schemas/salesforce.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/webhooks/schemas/whmcs.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/webhooks/webhooks.controller.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/webhooks/webhooks.module.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/bff/src/webhooks/webhooks.service.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/next.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/postcss.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/account/profile/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/api/health/route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/auth/forgot-password/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/auth/link-whmcs/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/auth/login/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/auth/reset-password/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/auth/set-password/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/auth/signup/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/billing/invoices/[id]/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/billing/invoices/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/billing/payments/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/catalog/esim/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/catalog/internet/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/catalog/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/catalog/vpn/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/checkout/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/dashboard/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/orders/[id]/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/subscriptions/[id]/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/subscriptions/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/support/cases/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/app/support/new/page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/auth/auth-layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/auth/session-timeout-warning.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/layout/dashboard-layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/layout/page-layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/ui/button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/ui/data-table.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/ui/input.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/ui/label.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/components/ui/search-filter-bar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/billing/components/InvoiceItemRow.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/billing/components/InvoiceStatusBadge.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/billing/components/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/billing/hooks/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/billing/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/dashboard/components/AccountStatusCard.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/dashboard/components/DashboardActivityItem.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/dashboard/components/QuickAction.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/dashboard/components/StatCard.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/dashboard/components/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/dashboard/hooks/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/dashboard/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/subscriptions/components/SubscriptionStatusBadge.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/subscriptions/components/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/subscriptions/hooks/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/features/subscriptions/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/hooks/useDashboard.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/hooks/useInvoices.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/hooks/useSubscriptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/lib/api.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/lib/auth/api.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/lib/auth/store.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/lib/env.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/lib/logger.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/lib/query-client.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/lib/utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/providers/query-provider.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/apps/portal/src/utils/currency.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/eslint.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/array-utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/case.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/common.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/invoice.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/logging/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/logging/logger.config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/logging/logger.interface.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/order.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/payment.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/skus.ts","messages":[{"ruleId":"@typescript-eslint/no-unsafe-assignment","severity":2,"message":"Unsafe assignment of an `any` value.","line":98,"column":11,"nodeType":"VariableDeclarator","messageId":"anyAssignment","endLine":98,"endColumn":35}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"// Central SKU registry for Product2 <-> portal mappings.\n// Replace the placeholder codes with your actual Product2.SKU__c values.\n\nexport type InternetTier = \"Platinum_Gold\" | \"Silver\";\nexport type AccessMode = \"IPoE-HGW\" | \"IPoE-BYOR\" | \"PPPoE\";\nexport type InstallPlan = \"One-time\" | \"12-Month\" | \"24-Month\";\n\nconst INTERNET_SKU: Record> = {\n Platinum_Gold: {\n \"IPoE-HGW\": \"INT-1G-PLAT-HGW\",\n \"IPoE-BYOR\": \"INT-1G-PLAT-BYOR\",\n PPPoE: \"INT-1G-PLAT-PPPOE\",\n },\n Silver: {\n \"IPoE-HGW\": \"INT-1G-SILV-HGW\",\n \"IPoE-BYOR\": \"INT-1G-SILV-BYOR\",\n PPPoE: \"INT-1G-SILV-PPPOE\",\n },\n};\n\nconst INSTALL_SKU: Record = {\n \"One-time\": \"INT-INSTALL-ONETIME\",\n \"12-Month\": \"INT-INSTALL-12M\",\n \"24-Month\": \"INT-INSTALL-24M\",\n};\n\nconst VPN_SKU: Record = {\n \"USA-SF\": \"VPN-USA-SF\",\n \"UK-London\": \"VPN-UK-LON\",\n};\n\nexport function getInternetServiceSku(tier: InternetTier, mode: AccessMode): string {\n return INTERNET_SKU[tier][mode];\n}\n\nexport function getInternetInstallSku(plan: InstallPlan): string {\n return INSTALL_SKU[plan];\n}\n\nexport function getVpnServiceSku(region: string): string {\n return VPN_SKU[region] || \"\";\n}\n\nexport function getVpnActivationSku(): string {\n return \"VPN-ACTIVATION\";\n}\n\n// ===== SIM / eSIM =====\nexport type SimFormat = \"eSIM\" | \"Physical\";\nexport type SimPlanType = \"DataOnly\" | \"DataSmsVoice\" | \"VoiceOnly\";\nexport type SimDataSize = \"5GB\" | \"10GB\" | \"25GB\" | \"50GB\" | \"None\";\n\ntype SimSkuMap = Record>>>;\n\n// Default mapping. Replace with your actual Product2.SKU__c codes in Salesforce.\n// Can be overridden at runtime via NEXT_PUBLIC_SIM_SKU_MAP (JSON with the same shape as SimSkuMap).\nconst DEFAULT_SIM_SKU_MAP: SimSkuMap = {\n eSIM: {\n DataOnly: {\n \"5GB\": \"SIM-ESIM-DATA-5GB\",\n \"10GB\": \"SIM-ESIM-DATA-10GB\",\n \"25GB\": \"SIM-ESIM-DATA-25GB\",\n \"50GB\": \"SIM-ESIM-DATA-50GB\",\n },\n DataSmsVoice: {\n \"5GB\": \"SIM-ESIM-VOICE-5GB\",\n \"10GB\": \"SIM-ESIM-VOICE-10GB\",\n \"25GB\": \"SIM-ESIM-VOICE-25GB\",\n \"50GB\": \"SIM-ESIM-VOICE-50GB\",\n },\n VoiceOnly: {\n None: \"SIM-ESIM-VOICEONLY\",\n },\n },\n Physical: {\n DataOnly: {\n \"5GB\": \"SIM-PHYS-DATA-5GB\",\n \"10GB\": \"SIM-PHYS-DATA-10GB\",\n \"25GB\": \"SIM-PHYS-DATA-25GB\",\n \"50GB\": \"SIM-PHYS-DATA-50GB\",\n },\n DataSmsVoice: {\n \"5GB\": \"SIM-PHYS-VOICE-5GB\",\n \"10GB\": \"SIM-PHYS-VOICE-10GB\",\n \"25GB\": \"SIM-PHYS-VOICE-25GB\",\n \"50GB\": \"SIM-PHYS-VOICE-50GB\",\n },\n VoiceOnly: {\n None: \"SIM-PHYS-VOICEONLY\",\n },\n },\n};\n\nfunction getEnvSimSkuMap(): SimSkuMap | null {\n try {\n const raw = process.env.NEXT_PUBLIC_SIM_SKU_MAP;\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n return parsed as SimSkuMap;\n } catch {\n return null;\n }\n}\n\n/**\n * Returns the Product2 SKU for the given SIM configuration.\n * If an environment override is present, it takes precedence.\n */\nexport function getSimServiceSku(\n format: SimFormat,\n planType: SimPlanType,\n dataSize: SimDataSize\n): string {\n const map = getEnvSimSkuMap() || DEFAULT_SIM_SKU_MAP;\n const byFormat = map[format] || {};\n const byType = (byFormat[planType] || {}) as Record;\n return typeof byType[dataSize] === \"string\" ? byType[dataSize] : \"\";\n}\n","usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/status.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/subscription.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/user.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/home/tnarantuya/projects/new-portal-website/packages/shared/src/validation.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}]