Merge branch 'main' into feature/user-management

This commit is contained in:
Julian Appel 2026-09-28 22:32:48 +02:00
commit 73579d1e79
22 changed files with 1141 additions and 54 deletions

View file

@ -40,7 +40,7 @@ export class CircuitStructureProjectCommandRepository
this.database,
input,
(tx) =>
this.applyCommand(
this.applyInTransaction(
tx,
input.projectId,
input.source,
@ -49,7 +49,11 @@ export class CircuitStructureProjectCommandRepository
);
}
private applyCommand(
/**
* Applies the circuit write inside an already-owned project transaction.
* Composite project commands use this without creating a second revision.
*/
applyInTransaction(
database: AppDatabase,
projectId: string,
source: ExecuteCircuitStructureCommandInput["source"],

View file

@ -1,4 +1,5 @@
import { and, asc, eq, inArray } from "drizzle-orm";
import { isDeepStrictEqual } from "node:util";
import {
assertDistributionBoardDeleteProjectCommand,
assertDistributionBoardInsertProjectCommand,
@ -17,6 +18,20 @@ import {
type DistributionBoardUpdateProjectCommand,
type DistributionBoardUpdateValues,
} from "../../domain/models/distribution-board-project-command.model.js";
import {
assertDistributionBoardFeederCircuitProjectCommand,
createDistributionBoardCreateFeederCircuitProjectCommand,
createDistributionBoardRemoveFeederCircuitProjectCommand,
distributionBoardCreateFeederCircuitCommandType,
distributionBoardRemoveFeederCircuitCommandType,
type DistributionBoardFeederCircuitProjectCommand,
} from "../../domain/models/distribution-board-feeder-circuit-project-command.model.js";
import {
circuitDeleteCommandType,
circuitInsertCommandType,
createCircuitDeleteProjectCommand,
createCircuitInsertProjectCommand,
} from "../../domain/models/circuit-structure-project-command.model.js";
import type {
DistributionBoardStructureProjectCommandStore,
ExecuteDistributionBoardStructureCommandInput,
@ -31,6 +46,7 @@ import { floors } from "../schema/floors.js";
import { projects } from "../schema/projects.js";
import { executeProjectCommandTransaction } from "./project-command-transaction.persistence.js";
import { assertBoardsAreNotReferencedAsFeeders } from "./distribution-board-feeding-deletion-guard.persistence.js";
import { CircuitStructureProjectCommandRepository } from "./circuit-structure-project-command.repository.js";
export class DistributionBoardStructureProjectCommandRepository
implements DistributionBoardStructureProjectCommandStore
@ -62,6 +78,30 @@ export class DistributionBoardStructureProjectCommandRepository
);
}
executeFeederCircuit(
input: Omit<
ExecuteDistributionBoardStructureCommandInput,
"command"
> & {
command: DistributionBoardFeederCircuitProjectCommand;
}
) {
assertDistributionBoardFeederCircuitProjectCommand(input.command);
if (
input.source === "user" &&
input.command.type === distributionBoardRemoveFeederCircuitCommandType
) {
throw new Error(
"Generated feeder circuits may only be removed through project history."
);
}
return executeProjectCommandTransaction(
this.database,
input,
(tx) => this.applyFeederCircuitCommand(tx, input)
);
}
private applyCommand(
database: AppDatabase,
projectId: string,
@ -367,6 +407,64 @@ export class DistributionBoardStructureProjectCommandRepository
return inverse;
}
private applyFeederCircuitCommand(
database: AppDatabase,
input: Omit<
ExecuteDistributionBoardStructureCommandInput,
"command"
> & {
command: DistributionBoardFeederCircuitProjectCommand;
}
): DistributionBoardFeederCircuitProjectCommand {
const circuitRepository =
new CircuitStructureProjectCommandRepository(database);
const { circuit, boardUpdate } = input.command.payload;
if (
input.command.type === distributionBoardCreateFeederCircuitCommandType
) {
const circuitInverse = circuitRepository.applyInTransaction(
database,
input.projectId,
input.source,
createCircuitInsertProjectCommand(circuit)
);
if (circuitInverse.type !== circuitDeleteCommandType) {
throw new Error("Created feeder circuit returned an invalid inverse.");
}
const boardInverse = this.update(database, {
...input,
command: boardUpdate,
});
return createDistributionBoardRemoveFeederCircuitProjectCommand(
circuit,
boardInverse
);
}
const boardInverse = this.update(database, {
...input,
command: boardUpdate,
});
const circuitInverse = circuitRepository.applyInTransaction(
database,
input.projectId,
input.source,
createCircuitDeleteProjectCommand(circuit.id, circuit.circuitListId)
);
if (
circuitInverse.type !== circuitInsertCommandType ||
!isDeepStrictEqual(circuitInverse.payload.circuit, circuit)
) {
throw new Error(
"Generated feeder circuit changed before history removal."
);
}
return createDistributionBoardCreateFeederCircuitProjectCommand(
circuit,
boardInverse
);
}
private assertFeedingReference(
database: AppDatabase,
projectId: string,