@workspace-fs/core
Version:
Multi-project workspace manager for Firesystem with support for multiple sources
178 lines (156 loc) • 4.77 kB
text/typescript
import type { Project, ProjectConfig, WorkspaceSettings } from "../types";
import { WorkspaceDatabase, StoredProject, WorkspaceState } from "../WorkspaceDatabase";
export class PersistenceManager {
constructor(
private database: WorkspaceDatabase,
private checkProjectAccessible: (projectId: string) => Promise<boolean>,
) {}
/**
* Restore workspace state from database
*/
async restoreWorkspaceState(): Promise<{
state: WorkspaceState | null;
activeProjectToLoad: StoredProject | null;
}> {
try {
// Get stored state
const state = await this.database.getWorkspaceState();
if (!state) return { state: null, activeProjectToLoad: null };
// Validate and clean orphaned references
let stateChanged = false;
// Validate activeProjectId
if (state.activeProjectId) {
const activeProjectAccessible = await this.checkProjectAccessible(
state.activeProjectId,
);
if (!activeProjectAccessible) {
console.warn(
`Removing orphaned activeProjectId: ${state.activeProjectId}`,
);
state.activeProjectId = null;
stateChanged = true;
}
}
// Validate recentProjectIds
if (state.recentProjectIds && state.recentProjectIds.length > 0) {
const validRecentIds: string[] = [];
for (const projectId of state.recentProjectIds) {
const projectAccessible = await this.checkProjectAccessible(projectId);
if (projectAccessible) {
validRecentIds.push(projectId);
} else {
console.warn(`Removing orphaned recentProjectId: ${projectId}`);
stateChanged = true;
}
}
state.recentProjectIds = validRecentIds;
}
// Save cleaned state if any orphaned references were found
if (stateChanged) {
await this.database.saveWorkspaceState(state);
console.info(
"Workspace state cleaned: removed orphaned project references",
);
}
// Get active project to load
let activeProjectToLoad: StoredProject | null = null;
if (state.activeProjectId) {
const storedProjects = await this.database.listProjects();
activeProjectToLoad = storedProjects.find(
(p) => p.id === state.activeProjectId,
) || null;
}
return { state, activeProjectToLoad };
} catch (error) {
console.error("Failed to restore workspace state:", error);
return { state: null, activeProjectToLoad: null };
}
}
/**
* Save current workspace state to database
*/
async saveWorkspaceState(
activeProjectId: string | null,
recentProjectIds: string[],
settings: WorkspaceSettings,
): Promise<void> {
const state: WorkspaceState = {
id: "current",
activeProjectId,
recentProjectIds,
settings,
};
await this.database.saveWorkspaceState(state);
}
/**
* Save project to database
*/
async saveProject(project: StoredProject): Promise<void> {
await this.database.saveProject(project);
}
/**
* Touch project in database
*/
async touchProject(projectId: string): Promise<void> {
await this.database.touchProject(projectId);
}
/**
* Get project from database
*/
async getProject(projectId: string): Promise<StoredProject | null> {
return this.database.getProject(projectId);
}
/**
* Delete project from database
*/
async deleteProject(projectId: string): Promise<void> {
await this.database.deleteProject(projectId);
}
/**
* Update project state in database
*/
async updateProjectState(
projectId: string,
state: { enabled?: boolean; disabledAt?: Date; enabledAt?: Date },
): Promise<void> {
await this.database.updateProjectState(projectId, state);
}
/**
* List all projects from database
*/
async listProjects(): Promise<StoredProject[]> {
return this.database.listProjects();
}
/**
* List recent projects
*/
async listRecentProjects(limit = 10): Promise<StoredProject[]> {
return this.database.listRecentProjects(limit);
}
/**
* Discover existing IndexedDB projects
*/
async discoverIndexedDBProjects(): Promise<ProjectConfig[]> {
const dbNames = await this.database.discoverIndexedDBProjects();
return dbNames.map((dbName) => ({
id: `discovered-${dbName}`,
name: dbName.replace(/^(firesystem-|@firesystem\/)/i, ""),
source: {
type: "indexeddb",
config: { dbName },
},
}));
}
/**
* Open database
*/
async open(): Promise<void> {
await this.database.open();
}
/**
* Close database
*/
close(): void {
this.database.close();
}
}