import { z } from "zod/v4"; import { ApplicationConfiguration } from "../../domain/application-configuration.aggregate"; import { ApplicationConfigurationRevision } from "../../domain/application-configuration-revision.entity"; import { APPLICATION_STATE_TRANSITIONS, ApplicationConfigurationInputSchema, } from "../../domain/application-configuration-values"; import { ApplicationConfigurationValues } from "../../domain/value-objects/application-configuration-values.vo"; import { applicationSnapshot } from "./configuration-snapshots"; import { snapshotValues } from "./scalar-snapshot-normalization"; import { persistedJson, profileIdentity } from "./work-profile-persistence.mapper"; export type ApplicationRow = { id: string; companyId: string; profileId: string; resourceId: string; values?: unknown; currentKey: string | null; }; export type ApplicationRevisionRow = { id: string; configurationId: string; position: number; values?: unknown; actorId: string; recordedAt: string; }; function mapped(raw: unknown) { const parsed = ApplicationConfigurationValues.create( ApplicationConfigurationInputSchema.parse(persistedJson(raw)) ); if (!parsed.isSuccess) { throw new Error("Valores de aplicación persistidos inconsistentes."); } return parsed.data; } export class ApplicationConfigurationPersistenceMapper { toDomain(row: ApplicationRow, revisions: readonly ApplicationRevisionRow[]) { const values = mapped(snapshotValues(applicationSnapshot, row)); const ordered = [...revisions].sort((a, b) => a.position - b.position); const history = ordered.map((revision, index) => { const value = mapped(snapshotValues(applicationSnapshot, revision)); const previous = ordered[index - 1]; if ( revision.configurationId !== row.id || revision.position !== index || !z.iso.datetime().safeParse(revision.recordedAt).success || revision.recordedAt < (previous?.recordedAt ?? "") || !values.sameTarget(value) ) { throw new Error("Historial de aplicación persistido inconsistente."); } return ApplicationConfigurationRevision.rehydrate( { values: value, actorId: profileIdentity(revision.actorId), recordedAt: revision.recordedAt, }, profileIdentity(revision.id) ); }); if (!(history.length && ["pending", "configured"].includes(history[0]?.values.state ?? ""))) { throw new Error("Estado inicial de aplicación persistido inconsistente."); } for (const [index, revision] of history.entries()) { const previous = history[index - 1]?.values; if ( previous && (!APPLICATION_STATE_TRANSITIONS[previous.state].some( (state) => state === revision.values.state ) || (previous.closed && !previous.equals(revision.values))) ) { throw new Error("Estados de aplicación históricos inconsistentes."); } } const keys = applicationKeys(values); if ( !history.at(-1)?.values.equals(values) || row.profileId !== values.profileId.toPrimitive() || row.resourceId !== values.resourceId.toPrimitive() || row.currentKey !== keys.currentKey ) { throw new Error("Referencias de aplicación persistidas inconsistentes."); } return ApplicationConfiguration.rehydrate( { companyId: profileIdentity(row.companyId), values, history }, profileIdentity(row.id) ); } toPersistence(item: ApplicationConfiguration) { const values = item.values.toPrimitive(); return { root: { id: item.id.toPrimitive(), companyId: item.companyId.toPrimitive(), ...values, ...applicationKeys(item.values), }, revisions: item.history.map((revision, position) => ({ id: revision.id.toPrimitive(), configurationId: item.id.toPrimitive(), position, actorId: revision.actorId.toPrimitive(), recordedAt: revision.recordedAt, ...revision.values.toPrimitive(), })), }; } } function applicationKeys(values: ApplicationConfigurationValues) { return { currentKey: values.closed ? null : `${values.profileId.toPrimitive()}:${values.resourceId.toPrimitive()}`, }; }