- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| src | -
-
- |
- 84.61% | -22/26 | -33.33% | -1/3 | -100% | -0/0 | -84.61% | -22/26 | -
| src/container | -
-
- |
- 100% | -82/82 | -100% | -14/14 | -100% | -10/10 | -100% | -82/82 | -
| src/models | -
-
- |
- 100% | -61/61 | -100% | -3/3 | -100% | -0/0 | -100% | -61/61 | -
| src/repositories/memory | -
-
- |
- 100% | -247/247 | -98.36% | -60/61 | -100% | -30/30 | -100% | -247/247 | -
| src/routes | -
-
- |
- 100% | -179/179 | -93.87% | -46/49 | -100% | -0/0 | -100% | -179/179 | -
| src/services | -
-
- |
- 100% | -170/170 | -100% | -36/36 | -100% | -15/15 | -100% | -170/170 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| File | -- | Statements | -- | Branches | -- | Functions | -- | Lines | -- |
|---|---|---|---|---|---|---|---|---|---|
| migrations.ts | -
-
- |
- 100% | -98/98 | -100% | -12/12 | -100% | -6/6 | -100% | -98/98 | -
| validate-schema.ts | -
-
- |
- 100% | -34/34 | -100% | -6/6 | -100% | -2/2 | -100% | -34/34 | -
- Press n or j to go to the next uncovered block, b, p or k for the previous block. -
- -| 1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 -97 -98 -99 | 1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -1x -5x -5x -5x -1x -1x -1x -1x -3x -3x -3x -3x -3x -3x -3x -3x -1x -1x -1x -1x -1x -6x -5x -5x -1x -1x -1x -1x -4x -4x -4x -4x -4x -4x -4x -4x -1x -1x -1x -1x -2x -2x -2x -2x -2x -2x -2x -2x -2x -2x -2x -2x -2x -2x -1x -1x -1x -1x -1x -2x -2x -2x -2x -2x -2x -2x -2x -2x -2x -2x -1x -1x -1x -1x -2x -2x - | import { readdir, readFile } from 'fs/promises';
-import { join } from 'path';
-import type { DbClient } from './client.js';
-
-const SCHEMA_MIGRATIONS_TABLE = `
-CREATE TABLE IF NOT EXISTS schema_migrations (
- version TEXT PRIMARY KEY,
- applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
-);
-`;
-
-/** Expected core tables that must exist after initial schema. */
-export const EXPECTED_TABLES = [
- 'borrowers',
- 'credit_lines',
- 'risk_evaluations',
- 'transactions',
- 'events',
-] as const;
-
-/**
- * Ensure schema_migrations table exists (for first-run or standalone migration).
- */
-export async function ensureSchemaMigrations(client: DbClient): Promise<void> {
- await client.query(SCHEMA_MIGRATIONS_TABLE);
-}
-
-/**
- * Return sorted list of migration filenames (e.g. 001_initial_schema.sql) in the given directory.
- */
-export async function listMigrationFiles(dir: string): Promise<string[]> {
- const entries = await readdir(dir, { withFileTypes: true });
- const files = entries
- .filter((e) => e.isFile() && e.name.endsWith('.sql'))
- .map((e) => e.name)
- .sort();
- return files;
-}
-
-/**
- * Extract version string from migration filename (e.g. 001_initial_schema.sql -> 001_initial_schema).
- */
-export function versionFromFilename(filename: string): string {
- if (!filename.endsWith('.sql')) return filename;
- return filename.slice(0, -4);
-}
-
-/**
- * Get applied migration versions from schema_migrations.
- */
-export async function getAppliedVersions(client: DbClient): Promise<string[]> {
- await ensureSchemaMigrations(client);
- const result = await client.query(
- 'SELECT version FROM schema_migrations ORDER BY version'
- );
- const rows = result.rows as { version: string }[];
- return rows.map((r) => r.version);
-}
-
-/**
- * Apply a single migration file (run its SQL and record version).
- */
-export async function applyMigration(
- client: DbClient,
- migrationsDir: string,
- filename: string
-): Promise<void> {
- const path = join(migrationsDir, filename);
- const sql = await readFile(path, 'utf-8');
- await client.query(sql);
- const version = versionFromFilename(filename);
- await client.query(
- 'INSERT INTO schema_migrations (version, applied_at) VALUES ($1, now()) ON CONFLICT (version) DO NOTHING',
- [version]
- );
-}
-
-/**
- * Run all pending migrations in the given directory.
- * Uses process.cwd() if migrationsDir is not absolute to resolve relative to cwd.
- */
-export async function runPendingMigrations(
- client: DbClient,
- migrationsDir: string
-): Promise<string[]> {
- const applied = await getAppliedVersions(client);
- const files = await listMigrationFiles(migrationsDir);
- const appliedSet = new Set(applied);
- const run: string[] = [];
- for (const file of files) {
- const version = versionFromFilename(file);
- if (appliedSet.has(version)) continue;
- await applyMigration(client, migrationsDir, file);
- run.push(version);
- appliedSet.add(version);
- }
- return run;
-}
- |