import type { SerializedProjectCommand } from "./project-command.model.js"; export const circuitDeviceRowInsertCommandType = "circuit-device-row.insert" as const; export const circuitDeviceRowDeleteCommandType = "circuit-device-row.delete" as const; export const circuitDeviceRowStructureCommandSchemaVersion = 1 as const; export interface CircuitDeviceRowSnapshot { id: string; circuitId: string; linkedProjectDeviceId: string | null; legacyConsumerId: string | null; sortOrder: number; name: string; displayName: string; phaseType: string | null; connectionKind: string | null; costGroup: string | null; category: string | null; level: string | null; roomId: string | null; roomNumberSnapshot: string | null; roomNameSnapshot: string | null; quantity: number; powerPerUnit: number; simultaneityFactor: number; cosPhi: number | null; remark: string | null; overriddenFields: string | null; } export interface CircuitDeviceRowInsertCommandPayload { row: CircuitDeviceRowSnapshot; } export interface CircuitDeviceRowDeleteCommandPayload { rowId: string; expectedCircuitId: string; } export interface CircuitDeviceRowInsertProjectCommand extends SerializedProjectCommand { schemaVersion: typeof circuitDeviceRowStructureCommandSchemaVersion; type: typeof circuitDeviceRowInsertCommandType; } export interface CircuitDeviceRowDeleteProjectCommand extends SerializedProjectCommand { schemaVersion: typeof circuitDeviceRowStructureCommandSchemaVersion; type: typeof circuitDeviceRowDeleteCommandType; } export type CircuitDeviceRowStructureProjectCommand = | CircuitDeviceRowInsertProjectCommand | CircuitDeviceRowDeleteProjectCommand; export function createCircuitDeviceRowInsertProjectCommand( row: CircuitDeviceRowSnapshot ): CircuitDeviceRowInsertProjectCommand { const command: CircuitDeviceRowInsertProjectCommand = { schemaVersion: circuitDeviceRowStructureCommandSchemaVersion, type: circuitDeviceRowInsertCommandType, payload: { row }, }; assertCircuitDeviceRowInsertProjectCommand(command); return command; } export function createCircuitDeviceRowDeleteProjectCommand( rowId: string, expectedCircuitId: string ): CircuitDeviceRowDeleteProjectCommand { const command: CircuitDeviceRowDeleteProjectCommand = { schemaVersion: circuitDeviceRowStructureCommandSchemaVersion, type: circuitDeviceRowDeleteCommandType, payload: { rowId, expectedCircuitId }, }; assertCircuitDeviceRowDeleteProjectCommand(command); return command; } export function assertCircuitDeviceRowInsertProjectCommand( command: SerializedProjectCommand ): asserts command is CircuitDeviceRowInsertProjectCommand { if ( command.schemaVersion !== circuitDeviceRowStructureCommandSchemaVersion || command.type !== circuitDeviceRowInsertCommandType ) { throw new Error("Unsupported circuit device-row insert command."); } if (!isPlainObject(command.payload)) { throw new Error("Circuit device-row insert payload must be an object."); } const row = command.payload.row; if (!isPlainObject(row)) { throw new Error("Circuit device-row insert command requires a row."); } assertNonEmptyString(row.id, "row.id"); assertNonEmptyString(row.circuitId, "row.circuitId"); assertNullableString(row.linkedProjectDeviceId, "row.linkedProjectDeviceId"); assertNullableString(row.legacyConsumerId, "row.legacyConsumerId"); assertFiniteNumber(row.sortOrder, "row.sortOrder"); assertNonEmptyString(row.name, "row.name"); assertNonEmptyString(row.displayName, "row.displayName"); for (const field of [ "phaseType", "connectionKind", "costGroup", "category", "level", "roomId", "roomNumberSnapshot", "roomNameSnapshot", "remark", "overriddenFields", ] as const) { assertNullableString(row[field], `row.${field}`); } assertNonNegativeNumber(row.quantity, "row.quantity"); assertNonNegativeNumber(row.powerPerUnit, "row.powerPerUnit"); assertNonNegativeNumber( row.simultaneityFactor, "row.simultaneityFactor" ); if (row.cosPhi !== null) { assertFiniteNumber(row.cosPhi, "row.cosPhi"); if (row.cosPhi <= 0) { throw new Error("row.cosPhi must be positive or null."); } } } export function assertCircuitDeviceRowDeleteProjectCommand( command: SerializedProjectCommand ): asserts command is CircuitDeviceRowDeleteProjectCommand { if ( command.schemaVersion !== circuitDeviceRowStructureCommandSchemaVersion || command.type !== circuitDeviceRowDeleteCommandType ) { throw new Error("Unsupported circuit device-row delete command."); } if (!isPlainObject(command.payload)) { throw new Error("Circuit device-row delete payload must be an object."); } assertNonEmptyString(command.payload.rowId, "rowId"); assertNonEmptyString( command.payload.expectedCircuitId, "expectedCircuitId" ); } function assertNonEmptyString(value: unknown, field: string) { if (typeof value !== "string" || !value.trim()) { throw new Error(`${field} must be a non-empty string.`); } } function assertNullableString(value: unknown, field: string) { if (value !== null && typeof value !== "string") { throw new Error(`${field} must be a string or null.`); } } 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 assertNonNegativeNumber(value: unknown, field: string) { assertFiniteNumber(value, field); if (value < 0) { throw new Error(`${field} must not be negative.`); } } function isPlainObject(value: unknown): value is Record { return value !== null && typeof value === "object" && !Array.isArray(value); }