/** * Page Response Mapper * * Maps API response schemas to domain DiscoveredPage objects. * Handles transformation from @archer/api-interface schemas → dashboard domain. * * @layer Infrastructure - API Client */ import type { PageResponse, PageListResponse, PageStatsResponse } from '@archer/api-interface'; import { DiscoveredPage, DiscoverySource, PageStatus, AnalysisStatus, } from '@/domain/entities/DiscoveredPage'; import type { PaginationMeta } from '../../shared/types'; /** * PageResponseMapper * * Transforms response DTOs to DiscoveredPage domain objects. * All mappings are internal - not exposed to application layer. */ export class PageResponseMapper { /** * Maps PageResponse schema to domain DiscoveredPage object * * @param response - PageResponse DTO from API * @returns DiscoveredPage domain entity */ static toPage(response: PageResponse): DiscoveredPage { // Map API response fields to domain entity const discoverySource = this.mapDiscoverySource(response.discoverySource); const status = this.mapPageStatus(response.status); const analysisStatus = this.mapAnalysisStatus(response.analysisStatus); return DiscoveredPage.fromPersistence({ id: response.id, tenantId: response.tenantId, url: response.url, normalizedUrl: response.normalizedUrl, title: response.title, discoverySource, discoveredAt: response.discoveredAt, depth: response.depth, status, analysisStatus, pageType: response.pageType, semanticLabel: response.semanticLabel, parentPageId: response.parentPageId, httpStatus: response.httpStatus, lastAnalyzedAt: response.lastAnalyzedAt, elementCount: response.elementCount ?? 0, addedToTraining: false, // Not provided by API response schema, default to false }); } /** * Maps PageListResponse schema to array of domain DiscoveredPages with pagination * * @param response - PageListResponse DTO from API * @returns Object with pages array and pagination metadata */ static toPageList(response: PageListResponse): { pages: DiscoveredPage[]; pagination: PaginationMeta; } { return { pages: response.data.map((item) => this.toPage(item)), pagination: { page: response.meta.page, limit: response.meta.limit, total: response.meta.total, totalPages: response.meta.totalPages, hasNext: response.meta.page < response.meta.totalPages, hasPrevious: response.meta.page > 1, }, }; } /** * Maps PageStatsResponse to statistics object * * @param response - PageStatsResponse DTO from API * @returns Page statistics object */ static toPageStats(response: PageStatsResponse): { totalPages: number; activePages: number; brokenPages: number; analyzedPages: number; pendingAnalysis: number; averageDepth: number; lastCrawledAt?: Date; } { return { totalPages: response.totalPages, activePages: response.activePages, brokenPages: response.brokenPages, analyzedPages: response.analyzedPages, pendingAnalysis: response.pendingAnalysis, averageDepth: response.averageDepth, lastCrawledAt: response.lastCrawledAt, }; } /** * Maps API discovery source to domain DiscoverySource enum * * API and domain use the same values: MANUAL, CRAWL, LINK_EXTRACTION, PLUGIN * Direct mapping without transformation. * * @param source - API discovery source string * @returns DiscoverySource enum value */ private static mapDiscoverySource( source: 'MANUAL' | 'CRAWL' | 'LINK_EXTRACTION' | 'PLUGIN' ): DiscoverySource { switch (source) { case 'MANUAL': return DiscoverySource.MANUAL; case 'CRAWL': return DiscoverySource.CRAWL; case 'LINK_EXTRACTION': return DiscoverySource.LINK_EXTRACTION; case 'PLUGIN': return DiscoverySource.PLUGIN; default: return DiscoverySource.CRAWL; } } /** * Maps API page status to domain PageStatus enum * * @param status - API page status string * @returns PageStatus enum value */ private static mapPageStatus( status: 'ACTIVE' | 'EXCLUDED' | 'BROKEN' | 'REDIRECT' ): PageStatus { switch (status) { case 'ACTIVE': return PageStatus.ACTIVE; case 'EXCLUDED': return PageStatus.EXCLUDED; case 'BROKEN': return PageStatus.BROKEN; case 'REDIRECT': return PageStatus.REDIRECT; default: return PageStatus.ACTIVE; } } /** * Maps API analysis status to domain AnalysisStatus enum * * @param status - API analysis status string * @returns AnalysisStatus enum value */ private static mapAnalysisStatus( status: 'NONE' | 'PENDING' | 'IN_PROGRESS' | 'ANALYZED' | 'OUTDATED' | 'FAILED' ): AnalysisStatus { switch (status) { case 'NONE': return AnalysisStatus.NONE; case 'PENDING': return AnalysisStatus.PENDING; case 'IN_PROGRESS': return AnalysisStatus.IN_PROGRESS; case 'ANALYZED': return AnalysisStatus.ANALYZED; case 'OUTDATED': return AnalysisStatus.OUTDATED; case 'FAILED': return AnalysisStatus.FAILED; default: return AnalysisStatus.NONE; } } }