import type { SerializedProjectCommand } from "./project-command.model.js"; export const circuitSectionReorderCommandType = "circuit.reorder-section" as const; export const circuitSectionReorderCommandSchemaVersion = 1 as const; export interface CircuitSectionReorderAssignment { circuitId: string; expectedSortOrder: number; targetSortOrder: number; } export interface CircuitSectionReorderCommandPayload { sectionId: string; assignments: CircuitSectionReorderAssignment[]; } export interface CircuitSectionReorderProjectCommand extends SerializedProjectCommand { schemaVersion: typeof circuitSectionReorderCommandSchemaVersion; type: typeof circuitSectionReorderCommandType; } export function createCircuitSectionReorderProjectCommand( sectionId: string, assignments: CircuitSectionReorderAssignment[] ): CircuitSectionReorderProjectCommand { const command: CircuitSectionReorderProjectCommand = { schemaVersion: circuitSectionReorderCommandSchemaVersion, type: circuitSectionReorderCommandType, payload: { sectionId, assignments }, }; assertCircuitSectionReorderProjectCommand(command); return command; } export function assertCircuitSectionReorderProjectCommand( command: SerializedProjectCommand ): asserts command is CircuitSectionReorderProjectCommand { if ( command.schemaVersion !== circuitSectionReorderCommandSchemaVersion || command.type !== circuitSectionReorderCommandType ) { throw new Error("Unsupported circuit section reorder command."); } if (!isPlainObject(command.payload)) { throw new Error( "Circuit section reorder payload must be an object." ); } const { sectionId, assignments } = command.payload; assertNonEmptyString(sectionId, "sectionId"); if (!Array.isArray(assignments) || assignments.length === 0) { throw new Error( "Circuit section reorder command requires assignments." ); } const circuitIds = new Set(); let hasChangedPosition = false; for (const assignment of assignments) { if (!isPlainObject(assignment)) { throw new Error( "Circuit section reorder command contains an invalid assignment." ); } assertNonEmptyString(assignment.circuitId, "circuitId"); assertFiniteNumber( assignment.expectedSortOrder, "expectedSortOrder" ); assertFiniteNumber( assignment.targetSortOrder, "targetSortOrder" ); if (circuitIds.has(assignment.circuitId)) { throw new Error( "Circuit section reorder command contains duplicate circuit ids." ); } if ( assignment.expectedSortOrder !== assignment.targetSortOrder ) { hasChangedPosition = true; } circuitIds.add(assignment.circuitId); } if (!hasChangedPosition) { throw new Error( "Circuit section reorder command must change at least one position." ); } } function assertNonEmptyString( value: unknown, field: string ): asserts value is string { if (typeof value !== "string" || !value.trim()) { throw new Error(`${field} must be a non-empty string.`); } } function assertFiniteNumber( value: unknown, field: string ): asserts value is number { if (typeof value !== "number" || !Number.isFinite(value)) { throw new Error(`${field} must be a finite number.`); } } function isPlainObject(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); }