Implements the end-to-end indexing pipeline with a SQLite-backed job queue, startup recovery, and REST API endpoints for job status. - IndexingPipeline: orchestrates crawl → parse → atomic replace → embed → repo stats update with progress tracking at each stage - JobQueue: sequential SQLite-backed queue (no external broker), deduplicates active jobs per repository, drains queued jobs on startup - startup.ts: stale job recovery (running→failed), repo state reset, singleton initialization wired from hooks.server.ts - GET /api/v1/jobs with repositoryId/status/limit filtering - GET /api/v1/jobs/[id] single job lookup - hooks.server.ts: initializes DB and pipeline on server start - 18 unit tests covering queue, pipeline stages, recovery, and atomicity Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
73 lines
2.5 KiB
TypeScript
73 lines
2.5 KiB
TypeScript
/**
|
|
* SvelteKit server hooks (TRUEREF-0009).
|
|
*
|
|
* Runs once when the Node.js server process starts. Initialises the database
|
|
* and kicks off the indexing pipeline + job queue so queued jobs resume
|
|
* automatically after a restart.
|
|
*/
|
|
|
|
import { initializeDatabase } from '$lib/server/db/index.js';
|
|
import { getClient } from '$lib/server/db/client.js';
|
|
import { initializePipeline } from '$lib/server/pipeline/startup.js';
|
|
import { EMBEDDING_CONFIG_KEY, createProviderFromConfig, defaultEmbeddingConfig } from '$lib/server/embeddings/factory.js';
|
|
import { EmbeddingService } from '$lib/server/embeddings/embedding.service.js';
|
|
import type { EmbeddingConfig } from '$lib/server/embeddings/factory.js';
|
|
import type { Handle } from '@sveltejs/kit';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Server initialisation — runs once on startup
|
|
// ---------------------------------------------------------------------------
|
|
|
|
try {
|
|
initializeDatabase();
|
|
|
|
const db = getClient();
|
|
|
|
// Load persisted embedding configuration (if any).
|
|
const configRow = db
|
|
.prepare<[string], { value: string }>(`SELECT value FROM settings WHERE key = ?`)
|
|
.get(EMBEDDING_CONFIG_KEY);
|
|
|
|
let embeddingService: EmbeddingService | null = null;
|
|
|
|
if (configRow) {
|
|
try {
|
|
const config: EmbeddingConfig =
|
|
typeof configRow.value === 'string'
|
|
? JSON.parse(configRow.value)
|
|
: (configRow.value as EmbeddingConfig);
|
|
|
|
if (config.provider !== 'none') {
|
|
const provider = createProviderFromConfig(config);
|
|
embeddingService = new EmbeddingService(db, provider);
|
|
}
|
|
} catch (err) {
|
|
console.warn(
|
|
`[hooks.server] Could not load embedding config: ${err instanceof Error ? err.message : String(err)}`
|
|
);
|
|
}
|
|
} else {
|
|
// Use the default (noop) config so the pipeline is still wired up.
|
|
const config = defaultEmbeddingConfig();
|
|
if (config.provider !== 'none') {
|
|
const provider = createProviderFromConfig(config);
|
|
embeddingService = new EmbeddingService(db, provider);
|
|
}
|
|
}
|
|
|
|
initializePipeline(db, embeddingService);
|
|
console.log('[hooks.server] Indexing pipeline initialised.');
|
|
} catch (err) {
|
|
console.error(
|
|
`[hooks.server] Failed to initialise server: ${err instanceof Error ? err.message : String(err)}`
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Request handler (pass-through)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const handle: Handle = async ({ event, resolve }) => {
|
|
return resolve(event);
|
|
};
|