Files
leistungsbilanz-ts/src/db/repositories/project-history.persistence.ts
T

122 lines
3.2 KiB
TypeScript

import { and, desc, eq, max } from "drizzle-orm";
import type { ProjectRevisionSource } from "../../domain/ports/project-revision.store.js";
import type { AppDatabase } from "../database-context.js";
import { projectHistoryStackEntries } from "../schema/project-history-stack-entries.js";
export interface ApplyProjectHistoryTransitionInput {
projectId: string;
source: ProjectRevisionSource;
recordedChangeSetId: string;
targetChangeSetId?: string;
}
export function applyProjectHistoryTransition(
database: AppDatabase,
input: ApplyProjectHistoryTransitionInput
) {
if (input.source === "user" || input.source === "restore") {
database
.delete(projectHistoryStackEntries)
.where(
and(
eq(projectHistoryStackEntries.projectId, input.projectId),
eq(projectHistoryStackEntries.stack, "redo")
)
)
.run();
push(
database,
input.projectId,
input.recordedChangeSetId,
"undo"
);
return;
}
if (input.source === "undo" || input.source === "redo") {
if (!input.targetChangeSetId) {
throw new Error(
`${input.source} requires a target change set.`
);
}
const sourceStack = input.source;
const targetStack = input.source === "undo" ? "redo" : "undo";
const top = database
.select({
changeSetId: projectHistoryStackEntries.changeSetId,
})
.from(projectHistoryStackEntries)
.where(
and(
eq(projectHistoryStackEntries.projectId, input.projectId),
eq(projectHistoryStackEntries.stack, sourceStack)
)
)
.orderBy(desc(projectHistoryStackEntries.position))
.limit(1)
.get();
if (!top || top.changeSetId !== input.targetChangeSetId) {
throw new Error(
`${input.source} target is not the top ${sourceStack} command.`
);
}
const nextPosition = getNextPosition(
database,
input.projectId,
targetStack
);
const moved = database
.update(projectHistoryStackEntries)
.set({ stack: targetStack, position: nextPosition })
.where(
and(
eq(projectHistoryStackEntries.projectId, input.projectId),
eq(
projectHistoryStackEntries.changeSetId,
input.targetChangeSetId
)
)
)
.run();
if (moved.changes !== 1) {
throw new Error("Project history stack changed during transition.");
}
}
}
function push(
database: AppDatabase,
projectId: string,
changeSetId: string,
stack: "undo" | "redo"
) {
database
.insert(projectHistoryStackEntries)
.values({
projectId,
changeSetId,
stack,
position: getNextPosition(database, projectId, stack),
})
.run();
}
function getNextPosition(
database: AppDatabase,
projectId: string,
stack: "undo" | "redo"
) {
const current = database
.select({ position: max(projectHistoryStackEntries.position) })
.from(projectHistoryStackEntries)
.where(
and(
eq(projectHistoryStackEntries.projectId, projectId),
eq(projectHistoryStackEntries.stack, stack)
)
)
.get();
return (current?.position ?? 0) + 1;
}