refactor: introduce domain model classes and mapper layer

Replace ad-hoc inline row casting (snake_case → camelCase) spread across
services, routes, and the indexing pipeline with explicit model classes
(Repository, IndexingJob, RepositoryVersion, Snippet, SearchResult) and
dedicated mapper classes that own the DB → domain conversion.

- Add src/lib/server/models/ with typed model classes for all domain entities
- Add src/lib/server/mappers/ with mapper classes per entity
- Remove duplicated RawRow interfaces and inline map functions from
  job-queue, repository.service, indexing.pipeline, and all API routes
- Add dtoJsonResponse helper to standardise JSON responses via SvelteKit json()
- Add api-contract.integration.test.ts as a regression baseline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Giancarmine Salucci
2026-03-25 14:29:49 +01:00
parent 7994254e23
commit 215cadf070
39 changed files with 1339 additions and 562 deletions

View File

@@ -0,0 +1,125 @@
export interface IndexingJobEntityProps {
id: string;
repository_id: string;
version_id: string | null;
status: 'queued' | 'running' | 'done' | 'failed';
progress: number;
total_files: number;
processed_files: number;
error: string | null;
started_at: number | null;
completed_at: number | null;
created_at: number;
}
export class IndexingJobEntity {
id: string;
repository_id: string;
version_id: string | null;
status: 'queued' | 'running' | 'done' | 'failed';
progress: number;
total_files: number;
processed_files: number;
error: string | null;
started_at: number | null;
completed_at: number | null;
created_at: number;
constructor(props: IndexingJobEntityProps) {
this.id = props.id;
this.repository_id = props.repository_id;
this.version_id = props.version_id;
this.status = props.status;
this.progress = props.progress;
this.total_files = props.total_files;
this.processed_files = props.processed_files;
this.error = props.error;
this.started_at = props.started_at;
this.completed_at = props.completed_at;
this.created_at = props.created_at;
}
}
export interface IndexingJobProps {
id: string;
repositoryId: string;
versionId: string | null;
status: 'queued' | 'running' | 'done' | 'failed';
progress: number;
totalFiles: number;
processedFiles: number;
error: string | null;
startedAt: Date | null;
completedAt: Date | null;
createdAt: Date;
}
export class IndexingJob {
id: string;
repositoryId: string;
versionId: string | null;
status: 'queued' | 'running' | 'done' | 'failed';
progress: number;
totalFiles: number;
processedFiles: number;
error: string | null;
startedAt: Date | null;
completedAt: Date | null;
createdAt: Date;
constructor(props: IndexingJobProps) {
this.id = props.id;
this.repositoryId = props.repositoryId;
this.versionId = props.versionId;
this.status = props.status;
this.progress = props.progress;
this.totalFiles = props.totalFiles;
this.processedFiles = props.processedFiles;
this.error = props.error;
this.startedAt = props.startedAt;
this.completedAt = props.completedAt;
this.createdAt = props.createdAt;
}
}
export interface IndexingJobDtoProps {
id: string;
repositoryId: string;
versionId: string | null;
status: 'queued' | 'running' | 'done' | 'failed';
progress: number;
totalFiles: number;
processedFiles: number;
error: string | null;
startedAt: Date | null;
completedAt: Date | null;
createdAt: Date;
}
export class IndexingJobDto {
id: string;
repositoryId: string;
versionId: string | null;
status: 'queued' | 'running' | 'done' | 'failed';
progress: number;
totalFiles: number;
processedFiles: number;
error: string | null;
startedAt: Date | null;
completedAt: Date | null;
createdAt: Date;
constructor(props: IndexingJobDtoProps) {
this.id = props.id;
this.repositoryId = props.repositoryId;
this.versionId = props.versionId;
this.status = props.status;
this.progress = props.progress;
this.totalFiles = props.totalFiles;
this.processedFiles = props.processedFiles;
this.error = props.error;
this.startedAt = props.startedAt;
this.completedAt = props.completedAt;
this.createdAt = props.createdAt;
}
}