@debugmcp/mcp-debugger
Version:
Step-through debugging MCP server for LLMs
156 lines • 4.75 kB
JavaScript
/**
* SessionStore - Pure data management for debug sessions
*
* This class is extracted from SessionManager to handle all session
* state management without external dependencies. This improves
* testability and follows the Single Responsibility Principle.
*/
import { v4 as uuidv4 } from 'uuid';
import { DebugLanguage, SessionState, SessionLifecycleState, DefaultAdapterPolicy, PythonAdapterPolicy, JsDebugAdapterPolicy, RustAdapterPolicy, MockAdapterPolicy } from '@debugmcp/shared';
import { SessionNotFoundError } from '../errors/debug-errors.js';
/**
* SessionStore manages the lifecycle and state of debug sessions
* without any external dependencies, making it highly testable.
*/
export class SessionStore {
sessions = new Map();
/**
* Selects the appropriate adapter policy based on language
*/
selectPolicy(language) {
switch (language) {
case DebugLanguage.PYTHON:
return PythonAdapterPolicy;
case DebugLanguage.JAVASCRIPT:
return JsDebugAdapterPolicy;
case DebugLanguage.RUST:
return RustAdapterPolicy;
case DebugLanguage.MOCK:
return MockAdapterPolicy;
default:
return DefaultAdapterPolicy;
}
}
/**
* Creates a new debug session
*/
createSession(params) {
const { language, name, executablePath } = params;
const sessionId = uuidv4();
const sessionName = name || `session-${sessionId.substring(0, 8)}`;
// Validate language
if (!Object.values(DebugLanguage).includes(language)) {
throw new Error(`Language '${language}' is not supported.`);
}
// Use policy to resolve executable path
const policy = this.selectPolicy(language);
const effectiveExecutablePath = policy.resolveExecutablePath(executablePath);
const session = {
id: sessionId,
name: sessionName,
language: language,
state: SessionState.CREATED,
createdAt: new Date(),
updatedAt: new Date(),
breakpoints: new Map(),
executablePath: effectiveExecutablePath,
proxyManager: undefined,
// Initialize new state model
sessionLifecycle: SessionLifecycleState.CREATED,
executionState: undefined,
};
this.sessions.set(sessionId, session);
return {
id: sessionId,
name: sessionName,
language: session.language,
state: session.state,
createdAt: session.createdAt,
updatedAt: session.updatedAt
};
}
/**
* Retrieves a session by ID
*/
get(sessionId) {
return this.sessions.get(sessionId);
}
/**
* Retrieves a session by ID, throwing if not found
*/
getOrThrow(sessionId) {
const session = this.sessions.get(sessionId);
if (!session) {
throw new SessionNotFoundError(sessionId);
}
return session;
}
/**
* Sets a session directly (for testing purposes)
*/
set(sessionId, session) {
this.sessions.set(sessionId, session);
}
/**
* Updates session fields
*/
update(sessionId, updates) {
const session = this.getOrThrow(sessionId);
Object.assign(session, updates);
session.updatedAt = new Date();
}
/**
* Updates only the session state
*/
updateState(sessionId, newState) {
const session = this.getOrThrow(sessionId);
if (session.state !== newState) {
session.state = newState;
session.updatedAt = new Date();
}
}
/**
* Removes a session
*/
remove(sessionId) {
return this.sessions.delete(sessionId);
}
/**
* Gets all sessions as DebugSessionInfo (public interface)
*/
getAll() {
return Array.from(this.sessions.values()).map(s => ({
id: s.id,
name: s.name,
language: s.language,
state: s.state,
createdAt: s.createdAt,
updatedAt: s.updatedAt
}));
}
/**
* Gets all sessions with full internal data
*/
getAllManaged() {
return Array.from(this.sessions.values());
}
/**
* Checks if a session exists
*/
has(sessionId) {
return this.sessions.has(sessionId);
}
/**
* Gets the number of sessions
*/
size() {
return this.sessions.size;
}
/**
* Clears all sessions
*/
clear() {
this.sessions.clear();
}
}
//# sourceMappingURL=session-store.js.map