Init
This commit is contained in:
343
src/lib/project-system/project-manager.ts
Normal file
343
src/lib/project-system/project-manager.ts
Normal file
@ -0,0 +1,343 @@
|
||||
import type { CsoundProject, CreateProjectData, UpdateProjectData, Result } from './types';
|
||||
import { projectDb } from './db';
|
||||
import { compressProject, decompressProject, projectToShareUrl, projectFromShareUrl } from './compression';
|
||||
|
||||
const CSOUND_VERSION = '7.0.0'; // This should be detected from @csound/browser
|
||||
|
||||
/**
|
||||
* Generate a unique ID for a project
|
||||
*/
|
||||
function generateId(): string {
|
||||
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current ISO timestamp
|
||||
*/
|
||||
function getCurrentTimestamp(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Project Manager - Main API for managing Csound projects
|
||||
*/
|
||||
export class ProjectManager {
|
||||
/**
|
||||
* Initialize the project manager (initializes database)
|
||||
*/
|
||||
async init(): Promise<void> {
|
||||
await projectDb.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new project
|
||||
*/
|
||||
async createProject(data: CreateProjectData): Promise<Result<CsoundProject>> {
|
||||
try {
|
||||
const now = getCurrentTimestamp();
|
||||
|
||||
const project: CsoundProject = {
|
||||
id: generateId(),
|
||||
title: data.title,
|
||||
author: data.author,
|
||||
dateCreated: now,
|
||||
dateModified: now,
|
||||
saveCount: 0,
|
||||
content: data.content || '',
|
||||
tags: data.tags || [],
|
||||
csoundVersion: CSOUND_VERSION,
|
||||
};
|
||||
|
||||
await projectDb.put(project);
|
||||
|
||||
return { success: true, data: project };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to create project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a project by ID
|
||||
*/
|
||||
async getProject(id: string): Promise<Result<CsoundProject>> {
|
||||
try {
|
||||
const project = await projectDb.get(id);
|
||||
|
||||
if (!project) {
|
||||
return {
|
||||
success: false,
|
||||
error: new Error(`Project not found: ${id}`),
|
||||
};
|
||||
}
|
||||
|
||||
return { success: true, data: project };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to get project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all projects
|
||||
*/
|
||||
async getAllProjects(): Promise<Result<CsoundProject[]>> {
|
||||
try {
|
||||
const projects = await projectDb.getAll();
|
||||
return { success: true, data: projects };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to get projects'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing project
|
||||
*/
|
||||
async updateProject(data: UpdateProjectData): Promise<Result<CsoundProject>> {
|
||||
try {
|
||||
const existingProject = await projectDb.get(data.id);
|
||||
|
||||
if (!existingProject) {
|
||||
return {
|
||||
success: false,
|
||||
error: new Error(`Project not found: ${data.id}`),
|
||||
};
|
||||
}
|
||||
|
||||
const updatedProject: CsoundProject = {
|
||||
...existingProject,
|
||||
...(data.title !== undefined && { title: data.title }),
|
||||
...(data.author !== undefined && { author: data.author }),
|
||||
...(data.content !== undefined && { content: data.content }),
|
||||
...(data.tags !== undefined && { tags: data.tags }),
|
||||
dateModified: getCurrentTimestamp(),
|
||||
saveCount: existingProject.saveCount + 1,
|
||||
};
|
||||
|
||||
await projectDb.put(updatedProject);
|
||||
|
||||
return { success: true, data: updatedProject };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to update project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a project
|
||||
*/
|
||||
async deleteProject(id: string): Promise<Result<void>> {
|
||||
try {
|
||||
await projectDb.delete(id);
|
||||
return { success: true, data: undefined };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to delete project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search projects by tag
|
||||
*/
|
||||
async getProjectsByTag(tag: string): Promise<Result<CsoundProject[]>> {
|
||||
try {
|
||||
const projects = await projectDb.getByTag(tag);
|
||||
return { success: true, data: projects };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to search projects by tag'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search projects by author
|
||||
*/
|
||||
async getProjectsByAuthor(author: string): Promise<Result<CsoundProject[]>> {
|
||||
try {
|
||||
const projects = await projectDb.getByAuthor(author);
|
||||
return { success: true, data: projects };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to search projects by author'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a project to a shareable URL
|
||||
*/
|
||||
async exportProjectToUrl(id: string, baseUrl?: string): Promise<Result<string>> {
|
||||
try {
|
||||
const project = await projectDb.get(id);
|
||||
|
||||
if (!project) {
|
||||
return {
|
||||
success: false,
|
||||
error: new Error(`Project not found: ${id}`),
|
||||
};
|
||||
}
|
||||
|
||||
const url = projectToShareUrl(project, baseUrl);
|
||||
|
||||
return { success: true, data: url };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to export project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export a project to compressed data (for copying to clipboard, etc.)
|
||||
*/
|
||||
async exportProjectToCompressed(id: string): Promise<Result<string>> {
|
||||
try {
|
||||
const project = await projectDb.get(id);
|
||||
|
||||
if (!project) {
|
||||
return {
|
||||
success: false,
|
||||
error: new Error(`Project not found: ${id}`),
|
||||
};
|
||||
}
|
||||
|
||||
const compressed = compressProject(project);
|
||||
|
||||
return { success: true, data: compressed.data };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to export project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a project from a URL
|
||||
*/
|
||||
async importProjectFromUrl(url: string): Promise<Result<CsoundProject>> {
|
||||
try {
|
||||
const project = projectFromShareUrl(url);
|
||||
|
||||
// Generate a new ID and reset timestamps
|
||||
const now = getCurrentTimestamp();
|
||||
const importedProject: CsoundProject = {
|
||||
...project,
|
||||
id: generateId(),
|
||||
dateCreated: now,
|
||||
dateModified: now,
|
||||
saveCount: 0,
|
||||
title: `${project.title} (imported)`,
|
||||
};
|
||||
|
||||
await projectDb.put(importedProject);
|
||||
|
||||
return { success: true, data: importedProject };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to import project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Import a project from compressed data
|
||||
*/
|
||||
async importProjectFromCompressed(compressedData: string): Promise<Result<CsoundProject>> {
|
||||
try {
|
||||
const project = decompressProject({
|
||||
data: compressedData,
|
||||
version: 1,
|
||||
});
|
||||
|
||||
// Generate a new ID and reset timestamps
|
||||
const now = getCurrentTimestamp();
|
||||
const importedProject: CsoundProject = {
|
||||
...project,
|
||||
id: generateId(),
|
||||
dateCreated: now,
|
||||
dateModified: now,
|
||||
saveCount: 0,
|
||||
title: `${project.title} (imported)`,
|
||||
};
|
||||
|
||||
await projectDb.put(importedProject);
|
||||
|
||||
return { success: true, data: importedProject };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to import project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a project
|
||||
*/
|
||||
async duplicateProject(id: string): Promise<Result<CsoundProject>> {
|
||||
try {
|
||||
const originalProject = await projectDb.get(id);
|
||||
|
||||
if (!originalProject) {
|
||||
return {
|
||||
success: false,
|
||||
error: new Error(`Project not found: ${id}`),
|
||||
};
|
||||
}
|
||||
|
||||
const now = getCurrentTimestamp();
|
||||
const duplicatedProject: CsoundProject = {
|
||||
...originalProject,
|
||||
id: generateId(),
|
||||
title: `${originalProject.title} (copy)`,
|
||||
dateCreated: now,
|
||||
dateModified: now,
|
||||
saveCount: 0,
|
||||
};
|
||||
|
||||
await projectDb.put(duplicatedProject);
|
||||
|
||||
return { success: true, data: duplicatedProject };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to duplicate project'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all projects (use with caution!)
|
||||
*/
|
||||
async clearAllProjects(): Promise<Result<void>> {
|
||||
try {
|
||||
await projectDb.clear();
|
||||
return { success: true, data: undefined };
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error('Failed to clear projects'),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const projectManager = new ProjectManager();
|
||||
Reference in New Issue
Block a user