All files / src/packlets/project-store index.ts

5.88% Statements 2/34
0% Branches 0/4
0% Functions 0/13
6.66% Lines 2/30

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                          1x                                                                                                                             1x                                
/**
 * @packageDocumentation
 *
 * IndexedDB persistence for project metadata (not file contents). Manages
 * the list of known projects with CRUD operations, slug generation, and
 * last-opened tracking.
 */
 
import { get, set } from "idb-keyval";
import { uuidv7 } from "uuidv7";
import type { Project, ProjectSource } from "./types";
import { slugify } from "./slugify";
 
const PROJECTS_KEY = "projects";
 
async function getAllProjects(): Promise<Project[]> {
  return (await get(PROJECTS_KEY)) ?? [];
}
 
async function saveProjects(projects: Project[]): Promise<void> {
  await set(PROJECTS_KEY, projects);
}
 
export async function listProjects(): Promise<Project[]> {
  const projects = await getAllProjects();
  return projects.toSorted(
    (a, b) => new Date(b.lastOpenedAt).getTime() - new Date(a.lastOpenedAt).getTime(),
  );
}
 
export async function addProject(displayName: string, source: ProjectSource): Promise<Project> {
  const projects = await getAllProjects();
  const baseSlug = slugify(displayName);
 
  let slug = baseSlug;
  let suffix = 1;
  while (projects.some((p) => p.slug === slug)) {
    slug = `${baseSlug}-${suffix}`;
    suffix++;
  }
 
  const now = new Date().toISOString();
  const project: Project = {
    id: uuidv7(),
    slug,
    displayName,
    source,
    createdAt: now,
    lastOpenedAt: now,
    lastUpdatedAt: now,
  };
 
  projects.push(project);
  await saveProjects(projects);
  return project;
}
 
export async function removeProject(slug: string): Promise<void> {
  const projects = await getAllProjects();
  await saveProjects(projects.filter((p) => p.slug !== slug));
}
 
export async function touchProject(slug: string): Promise<void> {
  const projects = await getAllProjects();
  const project = projects.find((p) => p.slug === slug);
  if (project) {
    project.lastOpenedAt = new Date().toISOString();
    await saveProjects(projects);
  }
}
 
export async function getProjectBySlug(slug: string): Promise<Project | undefined> {
  const projects = await getAllProjects();
  return projects.find((p) => p.slug === slug);
}
 
export const DEMO_SLUG = "__demo__";
 
export function createDemoProject(name: string): Project {
  const now = new Date().toISOString();
  return {
    id: uuidv7(),
    slug: DEMO_SLUG,
    displayName: name,
    source: { provider: "examples", name },
    createdAt: now,
    lastOpenedAt: now,
    lastUpdatedAt: now,
  };
}
 
export { createDemoProjectFile } from "./demo-project";