import { createHash } from "node:crypto"; import { UniqueID } from "@repo/rdx-ddd"; import { Maybe } from "@repo/rdx-utils"; import { z } from "zod/v4"; import { ProcessTemplate } from "../../domain/process-template.aggregate"; import { ProcessTemplateRevision } from "../../domain/process-template-revision.entity"; import { ProcessTemplateInputSchema } from "../../domain/process-values"; import { ProcessTemplateValues } from "../../domain/value-objects/process-template-values"; import { persistedJson } from "./work-profile-persistence.mapper"; export type ProcessTemplateRow = { id: string; nameKey: string; values: unknown }; export type ProcessTemplateRevisionRow = { id: string; itemId: string; position: number; actorId: string | null; recordedAt: string; values: unknown; }; function identity(value: string) { const parsed = UniqueID.create(value); if (!parsed.isSuccess) { throw new Error("Identidad de proceso inconsistente."); } return parsed.data; } function mapped(raw: unknown) { const values = ProcessTemplateValues.create(ProcessTemplateInputSchema.parse(persistedJson(raw))); if (!values.isSuccess) { throw new Error("Valores de proceso inconsistentes."); } return values.data; } export class ProcessTemplatePersistenceMapper { toDomain(row: ProcessTemplateRow, revisions: readonly ProcessTemplateRevisionRow[]) { const values = mapped(row.values); const ordered = [...revisions].sort((a, b) => a.position - b.position); const history = ordered.map((r, index) => { if ( r.itemId !== row.id || r.position !== index || !z.iso.datetime().safeParse(r.recordedAt).success || r.recordedAt < (ordered[index - 1]?.recordedAt ?? "") || (!r.actorId && index !== 0) ) { throw new Error("Historial de proceso inconsistente."); } return ProcessTemplateRevision.rehydrate( { values: mapped(r.values), actorId: r.actorId ? Maybe.some(identity(r.actorId)) : Maybe.none(), recordedAt: r.recordedAt, }, identity(r.id) ); }); if ( !(history.length && history.at(-1)?.values.equals(values)) || row.nameKey !== processTemplateNameKey(values.name.toPrimitive()) ) { throw new Error("RaĆ­z de proceso inconsistente."); } return ProcessTemplate.rehydrate({ values, history }, identity(row.id)); } toPersistence(item: ProcessTemplate) { const values = item.values.toPrimitive(); return { root: { id: item.id.toPrimitive(), nameKey: processTemplateNameKey(values.name), values: JSON.stringify(values), }, revisions: item.history.map((r, position) => ({ id: r.id.toPrimitive(), itemId: item.id.toPrimitive(), position, actorId: r.actorId.getOrUndefined()?.toPrimitive() ?? null, recordedAt: r.recordedAt, values: JSON.stringify(r.values.toPrimitive()), })), }; } } export function processTemplateNameKey(name: string) { return createHash("sha256").update(name.toLowerCase()).digest("hex"); }