Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| import { idb } from "$lib/remult.js"; | |
| import { snapshot } from "$lib/utils/object.svelte"; | |
| import { dequal } from "dequal"; | |
| import { Entity, Fields, repo } from "remult"; | |
| import { conversations, type ConversationEntityMembers } from "./conversations.svelte"; | |
| import { ProjectEntity, projects } from "./projects.svelte"; | |
| ("checkpoint") | |
| export class Checkpoint { | |
| .cuid() | |
| id!: string; | |
| .boolean() | |
| favorite: boolean = false; | |
| .createdAt() | |
| timestamp!: Date; | |
| .json() | |
| conversations: ConversationEntityMembers[] = []; | |
| .string() | |
| projectId!: string; | |
| } | |
| const checkpointsRepo = repo(Checkpoint, idb); | |
| class Checkpoints { | |
| #checkpoints: Record<ProjectEntity["id"], Checkpoint[]> = $state({}); | |
| for(projectId: ProjectEntity["id"]) { | |
| // Async load from db | |
| checkpointsRepo | |
| .find({ | |
| where: { | |
| projectId, | |
| }, | |
| }) | |
| .then(c => { | |
| // Dequal to avoid infinite loops | |
| if (dequal(c, this.#checkpoints[projectId])) return; | |
| this.#checkpoints[projectId] = c; | |
| }); | |
| return ( | |
| this.#checkpoints[projectId]?.toSorted((a, b) => { | |
| const aTime = a.timestamp?.getTime() ?? new Date().getTime(); | |
| const bTime = b.timestamp?.getTime() ?? new Date().getTime(); | |
| return bTime - aTime; | |
| }) ?? [] | |
| ); | |
| } | |
| async commit(projectId: ProjectEntity["id"]) { | |
| const project = projects.all.find(p => p.id == projectId); | |
| if (!project) return; | |
| const newCheckpoint = await checkpointsRepo.save( | |
| snapshot({ | |
| conversations: conversations.for(project.id).map(c => c.data), | |
| timestamp: new Date(), | |
| projectId: project.id, | |
| }), | |
| ); | |
| // Hack because dates are formatted to string by save | |
| newCheckpoint.conversations.forEach((c, i) => { | |
| newCheckpoint.conversations[i] = { ...c, createdAt: new Date(c.createdAt) }; | |
| }); | |
| const prev: Checkpoint[] = this.#checkpoints[projectId] ?? []; | |
| this.#checkpoints[projectId] = [...prev, newCheckpoint]; | |
| } | |
| restore(checkpoint: Checkpoint) { | |
| const cloned = snapshot(checkpoint); | |
| const modified = { | |
| ...cloned, | |
| conversations: cloned.conversations.map(c => ({ | |
| ...c, | |
| projectId: cloned.projectId, | |
| })), | |
| }; | |
| const project = projects.all.find(p => p.id == modified.projectId); | |
| if (!project) return; | |
| projects.activeId = modified.projectId; | |
| // conversations.deleteAllFrom(cloned.projectId); | |
| const prev = conversations.for(modified.projectId); | |
| modified.conversations.forEach((c, i) => { | |
| const prevC = prev[i]; | |
| if (prevC) return prevC.update({ ...c }); | |
| conversations.create({ | |
| ...c, | |
| projectId: modified.projectId, | |
| }); | |
| }); | |
| if (modified.conversations.length < prev.length) { | |
| prev.forEach((p, i) => { | |
| if (i < modified.conversations.length) return; | |
| conversations.delete(p.data); | |
| }); | |
| } | |
| } | |
| async toggleFavorite({ id, projectId }: Checkpoint) { | |
| if (!id) return; | |
| const p = await checkpointsRepo.findFirst({ id }); | |
| if (!p) return; | |
| await checkpointsRepo.update(id, { favorite: !p.favorite }); | |
| const prev: Checkpoint[] = snapshot(this.#checkpoints[projectId] ?? []); | |
| this.#checkpoints[projectId] = prev.map(c => { | |
| if (c.id !== id) return c; | |
| return { ...c, favorite: !c.favorite }; | |
| }); | |
| } | |
| async delete({ id, projectId }: Checkpoint) { | |
| if (!id) return; | |
| await checkpointsRepo.delete(id); | |
| const prev: Checkpoint[] = this.#checkpoints[projectId] ?? []; | |
| this.#checkpoints[projectId] = prev.filter(c => c.id != id); | |
| } | |
| async clear(projectId: ProjectEntity["id"]) { | |
| await checkpointsRepo.deleteMany({ where: { projectId } }); | |
| this.#checkpoints[projectId] = []; | |
| } | |
| async migrate(from: ProjectEntity["id"], to: ProjectEntity["id"]) { | |
| await checkpointsRepo.updateMany({ where: { projectId: from }, set: { projectId: to } }); | |
| const fromArr = snapshot(this.#checkpoints[from] ?? []); | |
| this.#checkpoints[to] = [ | |
| ...fromArr.map(c => ({ | |
| ...c, | |
| projectId: to, | |
| conversations: c.conversations.map(cn => ({ ...cn, projectId: to })), | |
| })), | |
| ]; | |
| this.#checkpoints[from] = []; | |
| } | |
| } | |
| export const checkpoints = new Checkpoints(); | |