refactor(ts): day 4 — middleware + utils + db .js → .ts (24 files)

All remaining backend files renamed:
  src/middleware/auth.ts, logging.ts (2 files)
  src/utils/*.ts         (20 files: ai, auditQueue, config, crypto,
                          embeddings, errors, fileType, logger, models,
                          notify, passwords, platform, promptSafe,
                          prompts, redact, sessions, transcribe*,
                          ttsGoogle)
  src/db/database.ts, migrate.ts (2 files)

Spot-fixes to satisfy tsc (all within the spirit of 'no behavior
change' — added `: any` annotations where the original JS relied on
duck typing that tsc's default inference narrows too aggressively):

  utils/ai.ts — body, converseParams, request literals + fallback
    result object + err.code/model/message casts. AI client has lots
    of provider-specific ad-hoc object shapes; Day 5 will replace the
    `any`s with proper provider-response interfaces.
  utils/embeddings.ts — payload + request as `any`; generateEmbedding
    call sites pass `undefined as any` for the now-required second
    arg (model) until we refactor the signature.
  utils/prompts.ts — PROMPTS typed as Record<string, any> so
    .loadFromDb / .updatePrompt / .getAllPrompts attachments after
    the const literal compile.
  utils/transcribeLocal.ts — buildArgs() has two `var args = [...]`
    in the same function scope (var-hoisted); both now typed as
    any[] so they don't type-clash across conditionals.

Backend is now 54 of 54 TypeScript files, permissive mode.
`npm run typecheck` EXIT 0. Prod container still running the old
JS image — no Dockerfile change yet.

Next: Day 5 flips strict: true, fixes every error tsc surfaces, adds
Vitest + Zod + Knip tooling.
This commit is contained in:
Daniel 2026-04-23 19:52:16 +02:00
parent 92a9a20a32
commit 6fa0d87da4
24 changed files with 19 additions and 19 deletions

View file

@ -223,7 +223,7 @@ async function callBedrock(messages, model, temperature, maxTokens) {
if (isAnthropic) {
// Anthropic Messages API format (native, best performance)
var InvokeModelCommand = BedrockModule.InvokeModelCommand;
var body = {
var body: any = {
anthropic_version: 'bedrock-2023-05-31',
max_tokens: maxTokens,
temperature: temperature,
@ -279,7 +279,7 @@ async function callBedrock(messages, model, temperature, maxTokens) {
return { role: m.role, content: [{ text: m.content }] };
});
var converseParams = {
var converseParams: any = {
modelId: modelId,
messages: converseMessages,
inferenceConfig: {
@ -346,7 +346,7 @@ async function callVertex(messages, model, temperature, maxTokens) {
}
});
var request = { contents: contents };
var request: any = { contents: contents };
if (systemInstruction) {
request.systemInstruction = { parts: [{ text: systemInstruction }] };
}
@ -418,8 +418,8 @@ async function callAI(messages, options) {
var allowed = await getAllowedModelIds(db);
if (allowed && allowed.size > 0 && !allowed.has(requestedModel)) {
var err = new Error('Model not permitted');
err.code = 'model_not_permitted';
err.model = requestedModel;
(err as any).code = 'model_not_permitted';
(err as any).model = requestedModel;
throw err;
}
} catch (e) {
@ -487,28 +487,28 @@ async function callAI(messages, options) {
if (activeProvider === 'openrouter' && model !== FALLBACK_MODEL && openrouter) {
logger.warn('Trying fallback model: ' + FALLBACK_MODEL);
try {
var fallbackResult = await callOpenRouter(messages, FALLBACK_MODEL, temperature, maxTokens);
var fallbackResult: any = await callOpenRouter(messages, FALLBACK_MODEL, temperature, maxTokens);
fallbackResult.fallback = true;
fallbackResult.duration = Date.now() - startTime;
logger.info('Fallback success', { model: FALLBACK_MODEL });
return fallbackResult;
} catch (err2) {
logger.error('Fallback also failed', { error: err2.message });
throw new Error('All models failed: ' + err2.message);
logger.error('Fallback also failed', { error: (err2 as any).message });
throw new Error('All models failed: ' + (err2 as any).message);
}
}
if (activeProvider === 'litellm' && model !== FALLBACK_MODEL && litellmClient) {
logger.warn('Trying fallback model on LiteLLM: ' + FALLBACK_MODEL);
try {
var litellmFallback = await callLiteLLM(messages, FALLBACK_MODEL, temperature, maxTokens);
var litellmFallback: any = await callLiteLLM(messages, FALLBACK_MODEL, temperature, maxTokens);
litellmFallback.fallback = true;
litellmFallback.duration = Date.now() - startTime;
logger.info('LiteLLM fallback success', { model: FALLBACK_MODEL });
return litellmFallback;
} catch (err3) {
logger.error('LiteLLM fallback also failed', { error: err3.message });
throw new Error('All models failed: ' + err3.message);
logger.error('LiteLLM fallback also failed', { error: (err3 as any).message });
throw new Error('All models failed: ' + (err3 as any).message);
}
}
}

View file

@ -67,7 +67,7 @@ async function generateEmbeddingLiteLLM(text, model, dimensions) {
headers['Authorization'] = 'Bearer ' + process.env.LITELLM_API_KEY;
}
var payload = {
var payload: any = {
model: model,
input: text
};
@ -113,7 +113,7 @@ async function generateEmbeddingVertexDirect(text, model, dimensions) {
var modelName = model.replace(/^vertex_ai\//, '');
// For text-embedding-005, we can specify output dimensions
var request = {
var request: any = {
instances: [{ content: text }]
};
@ -181,7 +181,7 @@ async function searchSimilar(queryText, opts) {
var db = require('../db/database');
// Generate embedding for query
var queryEmbedding = await generateEmbedding(queryText);
var queryEmbedding = await generateEmbedding(queryText, undefined as any);
// Build WHERE clause for filtering
var whereClause = 'WHERE c.published = true AND c.embedding IS NOT NULL';
@ -233,7 +233,7 @@ async function generateContentEmbedding(content) {
stripHtml(content.body || '').substring(0, 6000)
].filter(Boolean).join('\n\n');
return await generateEmbedding(text);
return await generateEmbedding(text, undefined as any);
}
/**

View file

@ -47,7 +47,7 @@ For ABNORMAL systems:
For NOT REVIEWED systems: simply state "Not reviewed" or "Not examined"
`;
const PROMPTS = {
const PROMPTS: Record<string, any> = {
// ======================== HPI ========================
hpiEncounter: `You are an expert pediatric medical scribe.

View file

@ -122,17 +122,17 @@ function transcribeWithLocal(audioBuffer, mimeType) {
/**
* Build command-line args based on the detected binary
*/
function buildArgs(binary, audioPath) {
function buildArgs(binary: string, audioPath: string): any[] {
if (binary.includes('faster-whisper')) {
// faster-whisper CLI
var args = ['--model', WHISPER_MODEL_SIZE, '--language', WHISPER_LANGUAGE];
var args: any[] = ['--model', WHISPER_MODEL_SIZE, '--language', WHISPER_LANGUAGE];
if (WHISPER_THREADS) args.push('--threads', WHISPER_THREADS);
args.push(audioPath);
return args;
}
// whisper.cpp style
var args = ['-f', audioPath, '-l', WHISPER_LANGUAGE, '-t', WHISPER_THREADS, '--no-timestamps'];
var args: any[] = ['-f', audioPath, '-l', WHISPER_LANGUAGE, '-t', WHISPER_THREADS, '--no-timestamps'];
if (WHISPER_MODEL_PATH) {
args.push('-m', WHISPER_MODEL_PATH);
} else {