chore(TRUEREF-0022): fix lint errors and update architecture docs
- Fix 15 ESLint errors across pipeline workers, SSE endpoints, and UI - Replace explicit any with proper entity types in worker entries - Remove unused imports and variables (basename, SSEEvent, getBroadcasterFn, seedRules) - Use empty catch clauses instead of unused error variables - Use SvelteSet for reactive Set state in repository page - Fix operator precedence in nullish coalescing expression - Replace $state+$effect with $derived for concurrency input - Use resolve() directly in href for navigation lint rule - Update ARCHITECTURE.md and FINDINGS.md for worker-thread architecture
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve as resolveRoute } from '$app/paths';
|
||||
import { resolve } from '$app/paths';
|
||||
|
||||
type RepositoryCardRepo = {
|
||||
id: string;
|
||||
@@ -38,10 +38,6 @@
|
||||
error: 'Error'
|
||||
};
|
||||
|
||||
const detailsHref = $derived(
|
||||
resolveRoute('/repos/[id]', { id: encodeURIComponent(repo.id) })
|
||||
);
|
||||
|
||||
const totalSnippets = $derived(repo.totalSnippets ?? 0);
|
||||
const trustScore = $derived(repo.trustScore ?? 0);
|
||||
const embeddingCount = $derived(repo.embeddingCount ?? 0);
|
||||
@@ -112,7 +108,7 @@
|
||||
{repo.state === 'indexing' ? 'Indexing...' : 'Re-index'}
|
||||
</button>
|
||||
<a
|
||||
href={detailsHref}
|
||||
href={resolve('/repos/[id]', { id: encodeURIComponent(repo.id) })}
|
||||
class="rounded-lg border border-gray-200 px-3 py-1.5 text-sm text-gray-700 hover:bg-gray-50"
|
||||
>
|
||||
Details
|
||||
|
||||
@@ -3,7 +3,7 @@ import Database from 'better-sqlite3';
|
||||
import { EmbeddingService } from '$lib/server/embeddings/embedding.service.js';
|
||||
import { createProviderFromProfile } from '$lib/server/embeddings/registry.js';
|
||||
import { EmbeddingProfileMapper } from '$lib/server/mappers/embedding-profile.mapper.js';
|
||||
import { EmbeddingProfileEntity } from '$lib/server/models/embedding-profile.js';
|
||||
import { EmbeddingProfileEntity, type EmbeddingProfileEntityProps } from '$lib/server/models/embedding-profile.js';
|
||||
import type { EmbedWorkerRequest, EmbedWorkerResponse, WorkerInitData } from './worker-types.js';
|
||||
|
||||
const { dbPath, embeddingProfileId } = workerData as WorkerInitData;
|
||||
@@ -35,7 +35,7 @@ if (!rawProfile) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const profileEntity = new EmbeddingProfileEntity(rawProfile as any);
|
||||
const profileEntity = new EmbeddingProfileEntity(rawProfile as EmbeddingProfileEntityProps);
|
||||
const profile = EmbeddingProfileMapper.fromEntity(profileEntity);
|
||||
|
||||
// Create provider and embedding service
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ProgressBroadcaster, type SSEEvent } from './progress-broadcaster.js';
|
||||
import { ProgressBroadcaster } from './progress-broadcaster.js';
|
||||
|
||||
describe('ProgressBroadcaster', () => {
|
||||
it('subscribe returns a readable stream', async () => {
|
||||
|
||||
@@ -16,7 +16,8 @@ import { LocalCrawler } from '$lib/server/crawler/local.crawler.js';
|
||||
import { IndexingPipeline } from './indexing.pipeline.js';
|
||||
import { JobQueue } from './job-queue.js';
|
||||
import { WorkerPool } from './worker-pool.js';
|
||||
import { initBroadcaster, getBroadcaster as getBroadcasterFn } from './progress-broadcaster.js';
|
||||
import type { ParseWorkerResponse } from './worker-types.js';
|
||||
import { initBroadcaster } from './progress-broadcaster.js';
|
||||
import type { ProgressBroadcaster } from './progress-broadcaster.js';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
@@ -101,7 +102,7 @@ export function initializePipeline(
|
||||
workerScript,
|
||||
embedWorkerScript,
|
||||
dbPath: options.dbPath,
|
||||
onProgress: (jobId: string, msg: any) => {
|
||||
onProgress: (jobId: string, msg: ParseWorkerResponse) => {
|
||||
// Update DB with progress
|
||||
db.prepare(
|
||||
`UPDATE indexing_jobs
|
||||
|
||||
@@ -4,7 +4,7 @@ import { IndexingPipeline } from './indexing.pipeline.js';
|
||||
import { crawl as githubCrawl } from '$lib/server/crawler/github.crawler.js';
|
||||
import { LocalCrawler } from '$lib/server/crawler/local.crawler.js';
|
||||
import { IndexingJobMapper } from '$lib/server/mappers/indexing-job.mapper.js';
|
||||
import { IndexingJobEntity } from '$lib/server/models/indexing-job.js';
|
||||
import { IndexingJobEntity, type IndexingJobEntityProps } from '$lib/server/models/indexing-job.js';
|
||||
import type { ParseWorkerRequest, ParseWorkerResponse, WorkerInitData } from './worker-types.js';
|
||||
import type { IndexingStage } from '$lib/types.js';
|
||||
|
||||
@@ -30,7 +30,7 @@ parentPort!.on('message', async (msg: ParseWorkerRequest) => {
|
||||
if (!rawJob) {
|
||||
throw new Error(`Job ${msg.jobId} not found`);
|
||||
}
|
||||
const job = IndexingJobMapper.fromEntity(new IndexingJobEntity(rawJob as any));
|
||||
const job = IndexingJobMapper.fromEntity(new IndexingJobEntity(rawJob as IndexingJobEntityProps));
|
||||
|
||||
await pipeline.run(
|
||||
job,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Worker } from 'node:worker_threads';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { basename } from 'node:path';
|
||||
import type { ParseWorkerRequest, ParseWorkerResponse, EmbedWorkerRequest, EmbedWorkerResponse, WorkerInitData } from './worker-types.js';
|
||||
|
||||
export interface WorkerPoolOptions {
|
||||
@@ -286,7 +285,7 @@ export class WorkerPool {
|
||||
for (const worker of this.workers) {
|
||||
try {
|
||||
worker.postMessage(msg);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Worker might already be exited
|
||||
}
|
||||
}
|
||||
@@ -296,7 +295,7 @@ export class WorkerPool {
|
||||
try {
|
||||
const embedMsg: EmbedWorkerRequest = { type: 'shutdown' };
|
||||
this.embedWorker.postMessage(embedMsg);
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Worker might already be exited
|
||||
}
|
||||
}
|
||||
@@ -317,7 +316,7 @@ export class WorkerPool {
|
||||
for (const worker of this.workers) {
|
||||
try {
|
||||
worker.terminate();
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Already terminated
|
||||
}
|
||||
}
|
||||
@@ -325,7 +324,7 @@ export class WorkerPool {
|
||||
if (this.embedWorker) {
|
||||
try {
|
||||
this.embedWorker.terminate();
|
||||
} catch (e) {
|
||||
} catch {
|
||||
// Already terminated
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,15 +217,6 @@ function seedEmbedding(client: Database.Database, snippetId: string, values: num
|
||||
.run(snippetId, values.length, Buffer.from(Float32Array.from(values).buffer), NOW_S);
|
||||
}
|
||||
|
||||
function seedRules(client: Database.Database, repositoryId: string, rules: string[]) {
|
||||
client
|
||||
.prepare(
|
||||
`INSERT INTO repository_configs (repository_id, rules, updated_at)
|
||||
VALUES (?, ?, ?)`
|
||||
)
|
||||
.run(repositoryId, JSON.stringify(rules), NOW_S);
|
||||
}
|
||||
|
||||
describe('API contract integration', () => {
|
||||
beforeEach(() => {
|
||||
db = createTestDb();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve as resolveRoute } from '$app/paths';
|
||||
import { onMount } from 'svelte';
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
import type { PageData } from './$types';
|
||||
import type { Repository, IndexingJob } from '$lib/types';
|
||||
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
|
||||
@@ -48,7 +49,7 @@
|
||||
// Discover tags state
|
||||
let discoverBusy = $state(false);
|
||||
let discoveredTags = $state<Array<{ tag: string; commitHash: string }>>([]);
|
||||
let selectedDiscoveredTags = $state<Set<string>>(new Set());
|
||||
let selectedDiscoveredTags = new SvelteSet<string>();
|
||||
let showDiscoverPanel = $state(false);
|
||||
let registerBusy = $state(false);
|
||||
|
||||
@@ -330,7 +331,7 @@
|
||||
discoveredTags = (d.tags ?? []).filter(
|
||||
(t: { tag: string; commitHash: string }) => !registeredTags.has(t.tag)
|
||||
);
|
||||
selectedDiscoveredTags = new Set(discoveredTags.map((t) => t.tag));
|
||||
selectedDiscoveredTags = new SvelteSet(discoveredTags.map((t) => t.tag));
|
||||
showDiscoverPanel = true;
|
||||
} catch (e) {
|
||||
errorMessage = (e as Error).message;
|
||||
@@ -340,13 +341,11 @@
|
||||
}
|
||||
|
||||
function toggleDiscoveredTag(tag: string) {
|
||||
const next = new Set(selectedDiscoveredTags);
|
||||
if (next.has(tag)) {
|
||||
next.delete(tag);
|
||||
if (selectedDiscoveredTags.has(tag)) {
|
||||
selectedDiscoveredTags.delete(tag);
|
||||
} else {
|
||||
next.add(tag);
|
||||
selectedDiscoveredTags.add(tag);
|
||||
}
|
||||
selectedDiscoveredTags = next;
|
||||
}
|
||||
|
||||
async function handleRegisterSelected() {
|
||||
@@ -381,7 +380,7 @@
|
||||
activeVersionJobs = next;
|
||||
showDiscoverPanel = false;
|
||||
discoveredTags = [];
|
||||
selectedDiscoveredTags = new Set();
|
||||
selectedDiscoveredTags = new SvelteSet();
|
||||
await loadVersions();
|
||||
} catch (e) {
|
||||
errorMessage = (e as Error).message;
|
||||
@@ -550,7 +549,7 @@
|
||||
onclick={() => {
|
||||
showDiscoverPanel = false;
|
||||
discoveredTags = [];
|
||||
selectedDiscoveredTags = new Set();
|
||||
selectedDiscoveredTags = new SvelteSet();
|
||||
}}
|
||||
class="text-xs text-blue-600 hover:underline"
|
||||
>
|
||||
@@ -649,7 +648,7 @@
|
||||
<div class="flex justify-between text-xs text-gray-500">
|
||||
<span>
|
||||
{#if job?.stageDetail}{job.stageDetail}{:else}{(job?.processedFiles ?? 0).toLocaleString()} / {(job?.totalFiles ?? 0).toLocaleString()} files{/if}
|
||||
{#if job?.stage}{' - ' + stageLabels[job.stage] ?? job.stage}{/if}
|
||||
{#if job?.stage}{' - ' + (stageLabels[job.stage] ?? job.stage)}{/if}
|
||||
</span>
|
||||
<span>{job?.progress ?? 0}%</span>
|
||||
</div>
|
||||
|
||||
@@ -66,16 +66,12 @@
|
||||
let saveError = $state<string | null>(null);
|
||||
let saveStatusTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
let concurrencyInput = $state<number>(0);
|
||||
let concurrencyInput = $derived(data.indexingConcurrency);
|
||||
let concurrencySaving = $state(false);
|
||||
let concurrencySaveStatus = $state<'idle' | 'ok' | 'error'>('idle');
|
||||
let concurrencySaveError = $state<string | null>(null);
|
||||
let concurrencySaveStatusTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
$effect(() => {
|
||||
concurrencyInput = data.indexingConcurrency;
|
||||
});
|
||||
|
||||
const currentSettings = $derived(settingsOverride ?? data.settings);
|
||||
const activeProfile = $derived(currentSettings.activeProfile);
|
||||
const activeConfigEntries = $derived(activeProfile?.configEntries ?? []);
|
||||
|
||||
Reference in New Issue
Block a user