mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 08:31:19 +00:00
Break the 1,870-line monolithic stats-db.ts into 13 focused modules under src/main/stats/, following the same pattern used in the agents module refactor. Key improvements: - StatementCache for prepared statement reuse across CRUD operations - DB guard accessor (get database()) replaces 18 repeated null checks - Transaction-wrapped clearOldData for atomic multi-table cleanup - _meta table replaces external file for vacuum timestamp storage - Row mapper functions centralize snake_case-to-camelCase conversion - runStatements() helper eliminates repeated split-by-semicolon patterns - Migration functions accept db param instead of relying on class context - CSV export adds isRemote column and RFC 4180 compliant escaping - Statement caches properly cleared on database close Split the 6,400-line monolithic test file into 8 focused test files under src/__tests__/main/stats/ matching the source module structure.
106 lines
2.7 KiB
TypeScript
106 lines
2.7 KiB
TypeScript
/**
|
|
* Session Lifecycle CRUD Operations
|
|
*
|
|
* Tracks when sessions are created (launched) and closed,
|
|
* enabling session duration and lifecycle analytics.
|
|
*/
|
|
|
|
import type Database from 'better-sqlite3';
|
|
import type { SessionLifecycleEvent, StatsTimeRange } from '../../shared/stats-types';
|
|
import { generateId, getTimeRangeStart, normalizePath, LOG_CONTEXT } from './utils';
|
|
import { mapSessionLifecycleRow, type SessionLifecycleRow } from './row-mappers';
|
|
import { StatementCache } from './utils';
|
|
import { logger } from '../utils/logger';
|
|
|
|
const stmtCache = new StatementCache();
|
|
|
|
const INSERT_SQL = `
|
|
INSERT INTO session_lifecycle (id, session_id, agent_type, project_path, created_at, is_remote)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
`;
|
|
|
|
/**
|
|
* Record a session being created (launched)
|
|
*/
|
|
export function recordSessionCreated(
|
|
db: Database.Database,
|
|
event: Omit<SessionLifecycleEvent, 'id' | 'closedAt' | 'duration'>
|
|
): string {
|
|
const id = generateId();
|
|
const stmt = stmtCache.get(db, INSERT_SQL);
|
|
|
|
stmt.run(
|
|
id,
|
|
event.sessionId,
|
|
event.agentType,
|
|
normalizePath(event.projectPath),
|
|
event.createdAt,
|
|
event.isRemote !== undefined ? (event.isRemote ? 1 : 0) : null
|
|
);
|
|
|
|
logger.debug(`Recorded session created: ${event.sessionId}`, LOG_CONTEXT);
|
|
return id;
|
|
}
|
|
|
|
/**
|
|
* Record a session being closed
|
|
*/
|
|
export function recordSessionClosed(
|
|
db: Database.Database,
|
|
sessionId: string,
|
|
closedAt: number
|
|
): boolean {
|
|
// Get the session's created_at time to calculate duration
|
|
const session = db
|
|
.prepare('SELECT created_at FROM session_lifecycle WHERE session_id = ?')
|
|
.get(sessionId) as { created_at: number } | undefined;
|
|
|
|
if (!session) {
|
|
logger.debug(`Session not found for closure: ${sessionId}`, LOG_CONTEXT);
|
|
return false;
|
|
}
|
|
|
|
const duration = closedAt - session.created_at;
|
|
|
|
const stmt = stmtCache.get(
|
|
db,
|
|
`
|
|
UPDATE session_lifecycle
|
|
SET closed_at = ?, duration = ?
|
|
WHERE session_id = ?
|
|
`
|
|
);
|
|
|
|
const result = stmt.run(closedAt, duration, sessionId);
|
|
logger.debug(`Recorded session closed: ${sessionId}, duration: ${duration}ms`, LOG_CONTEXT);
|
|
return result.changes > 0;
|
|
}
|
|
|
|
/**
|
|
* Get session lifecycle events within a time range
|
|
*/
|
|
export function getSessionLifecycleEvents(
|
|
db: Database.Database,
|
|
range: StatsTimeRange
|
|
): SessionLifecycleEvent[] {
|
|
const startTime = getTimeRangeStart(range);
|
|
const stmt = stmtCache.get(
|
|
db,
|
|
`
|
|
SELECT * FROM session_lifecycle
|
|
WHERE created_at >= ?
|
|
ORDER BY created_at DESC
|
|
`
|
|
);
|
|
|
|
const rows = stmt.all(startTime) as SessionLifecycleRow[];
|
|
return rows.map(mapSessionLifecycleRow);
|
|
}
|
|
|
|
/**
|
|
* Clear the statement cache (call when database is closed)
|
|
*/
|
|
export function clearSessionLifecycleCache(): void {
|
|
stmtCache.clear();
|
|
}
|