Create clean release database baseline

This commit is contained in:
2026-07-31 14:07:26 +02:00
parent 5bee3cb103
commit 734a2bcfc4
129 changed files with 2121 additions and 30607 deletions
@@ -10,7 +10,6 @@ export interface CircuitDeviceRowSnapshot {
id: string;
circuitId: string;
linkedProjectDeviceId: string | null;
legacyConsumerId: string | null;
sortOrder: number;
name: string;
displayName: string;
@@ -101,7 +100,6 @@ export function assertCircuitDeviceRowInsertProjectCommand(
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");
@@ -2,7 +2,6 @@ export interface CircuitDeviceRow {
id: string;
circuitId: string;
linkedProjectDeviceId?: string;
legacyConsumerId?: string;
sortOrder: number;
name: string;
displayName: string;
@@ -8,9 +8,6 @@ export interface CircuitUpdateValues {
equipmentIdentifier: string;
displayName: string | null;
sortOrder: number;
protectionType: string | null;
protectionRatedCurrent: number | null;
protectionCharacteristic: string | null;
cableType: string | null;
cableCrossSection: string | null;
cableLength: number | null;
@@ -118,7 +115,6 @@ function assertCircuitUpdateFieldValue(
return;
}
if (
field === "protectionRatedCurrent" ||
field === "cableLength" ||
field === "voltage"
) {
@@ -147,9 +143,6 @@ function isCircuitUpdateField(value: string): value is CircuitUpdateField {
"equipmentIdentifier",
"displayName",
"sortOrder",
"protectionType",
"protectionRatedCurrent",
"protectionCharacteristic",
"cableType",
"cableCrossSection",
"cableLength",
@@ -21,9 +21,6 @@ export interface CircuitSnapshot {
equipmentIdentifier: string;
displayName: string | null;
sortOrder: number;
protectionType: string | null;
protectionRatedCurrent: number | null;
protectionCharacteristic: string | null;
cableType: string | null;
cableCrossSection: string | null;
cableLength: number | null;
@@ -115,8 +112,6 @@ export function assertCircuitInsertProjectCommand(
assertNullableString(circuit.displayName, "circuit.displayName");
assertFiniteNumber(circuit.sortOrder, "circuit.sortOrder");
for (const field of [
"protectionType",
"protectionCharacteristic",
"cableType",
"cableCrossSection",
"rcdAssignment",
@@ -127,10 +122,6 @@ export function assertCircuitInsertProjectCommand(
] as const) {
assertNullableString(circuit[field], `circuit.${field}`);
}
assertNullableNonNegativeNumber(
circuit.protectionRatedCurrent,
"circuit.protectionRatedCurrent"
);
assertNullableNonNegativeNumber(
circuit.cableLength,
"circuit.cableLength"
-15
View File
@@ -1,7 +1,6 @@
export interface CircuitTreeDeviceRow {
id: string;
linkedProjectDeviceId?: string;
legacyConsumerId?: string;
sortOrder: number;
name: string;
displayName: string;
@@ -29,9 +28,6 @@ export interface CircuitTreeCircuit {
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
protectionType?: string;
protectionRatedCurrent?: number;
protectionCharacteristic?: string;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
@@ -73,17 +69,6 @@ export interface CircuitTreeResponse {
footerComponents: CircuitTreeComponent[];
}
export interface LegacyMigrationReport {
circuitListId: string;
legacyConsumerCount: number;
createdCircuitCount: number;
createdDeviceRowCount: number;
groupedDuplicateCircuitNumbers: Array<{ normalizedCircuitNumber: string; count: number }>;
generatedIdentifiers: string[];
unassignedRows: Array<{ consumerId: string; reason: string }>;
warnings: string[];
}
import type { CircuitGroupCategory } from "../../shared/constants/circuit-group.js";
import type {
DistributionBoardComponentPlacement,
-3
View File
@@ -5,9 +5,6 @@ export interface Circuit {
equipmentIdentifier: string;
displayName?: string;
sortOrder: number;
protectionType?: string;
protectionRatedCurrent?: number;
protectionCharacteristic?: string;
cableType?: string;
cableCrossSection?: string;
cableLength?: number;
@@ -6,8 +6,7 @@ import type { SerializedProjectCommand } from "./project-command.model.js";
export const distributionBoardUpdateCommandType =
"distribution-board.update" as const;
export const legacyDistributionBoardUpdateCommandSchemaVersion = 1 as const;
export const distributionBoardUpdateCommandSchemaVersion = 2 as const;
export const distributionBoardUpdateCommandSchemaVersion = 1 as const;
export interface DistributionBoardUpdateValues {
floorId: string | null;
@@ -33,27 +32,17 @@ export interface DistributionBoardUpdateCommandPayload {
changes: DistributionBoardUpdateFieldChange[];
}
interface CurrentDistributionBoardUpdateProjectCommand
export interface DistributionBoardUpdateProjectCommand
extends SerializedProjectCommand<DistributionBoardUpdateCommandPayload> {
schemaVersion: typeof distributionBoardUpdateCommandSchemaVersion;
type: typeof distributionBoardUpdateCommandType;
}
interface LegacyDistributionBoardUpdateProjectCommand
extends SerializedProjectCommand<DistributionBoardUpdateCommandPayload> {
schemaVersion: typeof legacyDistributionBoardUpdateCommandSchemaVersion;
type: typeof distributionBoardUpdateCommandType;
}
export type DistributionBoardUpdateProjectCommand =
| CurrentDistributionBoardUpdateProjectCommand
| LegacyDistributionBoardUpdateProjectCommand;
export function createDistributionBoardUpdateProjectCommand(
distributionBoardId: string,
patch: DistributionBoardUpdatePatch
): CurrentDistributionBoardUpdateProjectCommand {
const command: CurrentDistributionBoardUpdateProjectCommand = {
): DistributionBoardUpdateProjectCommand {
const command: DistributionBoardUpdateProjectCommand = {
schemaVersion: distributionBoardUpdateCommandSchemaVersion,
type: distributionBoardUpdateCommandType,
payload: {
@@ -72,9 +61,7 @@ export function assertDistributionBoardUpdateProjectCommand(
command: SerializedProjectCommand<unknown>
): asserts command is DistributionBoardUpdateProjectCommand {
if (
(command.schemaVersion !==
legacyDistributionBoardUpdateCommandSchemaVersion &&
command.schemaVersion !== distributionBoardUpdateCommandSchemaVersion) ||
command.schemaVersion !== distributionBoardUpdateCommandSchemaVersion ||
command.type !== distributionBoardUpdateCommandType ||
!isPlainObject(command.payload)
) {
@@ -97,9 +84,7 @@ export function assertDistributionBoardUpdateProjectCommand(
!isPlainObject(change) ||
(change.field !== "floorId" &&
change.field !== "supplyType" &&
(command.schemaVersion ===
legacyDistributionBoardUpdateCommandSchemaVersion ||
change.field !== "simultaneityFactor")) ||
change.field !== "simultaneityFactor") ||
seen.has(change.field)
) {
throw new Error(
@@ -18,36 +18,7 @@ export const distributionBoardInsertCommandType =
"distribution-board.insert" as const;
export const distributionBoardDeleteCommandType =
"distribution-board.delete" as const;
export const legacyDistributionBoardStructureCommandSchemaVersion = 1 as const;
export const previousDistributionBoardStructureCommandSchemaVersion = 2 as const;
export const distributionBoardStructureCommandSchemaVersion = 3 as const;
const legacyDefaultCircuitSectionDefinitions = [
{
key: "lighting",
displayName: "Lighting",
prefix: "-1F",
sortOrder: 10,
},
{
key: "single_phase",
displayName: "Single-phase circuits",
prefix: "-2F",
sortOrder: 20,
},
{
key: "three_phase",
displayName: "Three-phase circuits",
prefix: "-3F",
sortOrder: 30,
},
{
key: "unassigned",
displayName: "Unassigned",
prefix: "-UF",
sortOrder: 90,
},
] as const;
export const distributionBoardStructureCommandSchemaVersion = 1 as const;
export const defaultCircuitSectionDefinitions = [
{
@@ -76,31 +47,6 @@ export const defaultCircuitSectionDefinitions = [
},
] as const;
interface LegacyDistributionBoardStructureSnapshot {
distributionBoard: {
id: string;
projectId: string;
name: string;
};
circuitList: DistributionBoardStructureSnapshot["circuitList"];
sections: LegacyCircuitSectionSnapshot[];
}
interface PreviousDistributionBoardStructureSnapshot {
distributionBoard: DistributionBoardStructureSnapshot["distributionBoard"];
circuitList: DistributionBoardStructureSnapshot["circuitList"];
sections: LegacyCircuitSectionSnapshot[];
}
interface LegacyCircuitSectionSnapshot {
id: string;
circuitListId: string;
key: string;
displayName: string;
prefix: string;
sortOrder: number;
}
export interface DistributionBoardStructureSnapshot {
distributionBoard: {
id: string;
@@ -140,22 +86,15 @@ export interface DistributionBoardStructureSnapshot {
export interface NormalizedDistributionBoardStructureSnapshot {
distributionBoard: DistributionBoardStructureSnapshot["distributionBoard"];
circuitList: DistributionBoardStructureSnapshot["circuitList"];
sections: Array<
LegacyCircuitSectionSnapshot & {
category: CircuitGroupCategory | null;
groupNumber: number | null;
}
>;
sections: DistributionBoardStructureSnapshot["sections"];
components: DistributionBoardStructureSnapshot["components"];
}
interface DistributionBoardStructureCommandPayload<
TStructure = DistributionBoardStructureSnapshot,
> {
structure: TStructure;
interface DistributionBoardStructureCommandPayload {
structure: DistributionBoardStructureSnapshot;
}
interface CurrentDistributionBoardStructureProjectCommand
interface DistributionBoardStructureProjectCommandBase
extends SerializedProjectCommand<DistributionBoardStructureCommandPayload> {
schemaVersion: typeof distributionBoardStructureCommandSchemaVersion;
type:
@@ -163,41 +102,19 @@ interface CurrentDistributionBoardStructureProjectCommand
| typeof distributionBoardDeleteCommandType;
}
interface LegacyDistributionBoardStructureProjectCommand
extends SerializedProjectCommand<
DistributionBoardStructureCommandPayload<LegacyDistributionBoardStructureSnapshot>
> {
schemaVersion: typeof legacyDistributionBoardStructureCommandSchemaVersion;
type:
| typeof distributionBoardInsertCommandType
| typeof distributionBoardDeleteCommandType;
}
interface PreviousDistributionBoardStructureProjectCommand
extends SerializedProjectCommand<
DistributionBoardStructureCommandPayload<PreviousDistributionBoardStructureSnapshot>
> {
schemaVersion: typeof previousDistributionBoardStructureCommandSchemaVersion;
type:
| typeof distributionBoardInsertCommandType
| typeof distributionBoardDeleteCommandType;
}
export interface DistributionBoardInsertProjectCommand
extends CurrentDistributionBoardStructureProjectCommand {
extends DistributionBoardStructureProjectCommandBase {
type: typeof distributionBoardInsertCommandType;
}
export interface DistributionBoardDeleteProjectCommand
extends CurrentDistributionBoardStructureProjectCommand {
extends DistributionBoardStructureProjectCommandBase {
type: typeof distributionBoardDeleteCommandType;
}
export type DistributionBoardStructureProjectCommand =
| DistributionBoardInsertProjectCommand
| DistributionBoardDeleteProjectCommand
| PreviousDistributionBoardStructureProjectCommand
| LegacyDistributionBoardStructureProjectCommand;
| DistributionBoardDeleteProjectCommand;
export function createDistributionBoardStructureSnapshot(
projectId: string,
@@ -296,35 +213,6 @@ export function assertDistributionBoardDeleteProjectCommand(
export function normalizeDistributionBoardStructureProjectCommand(
command: DistributionBoardStructureProjectCommand
): NormalizedDistributionBoardStructureSnapshot {
if (
command.schemaVersion ===
legacyDistributionBoardStructureCommandSchemaVersion
) {
return {
distributionBoard: {
...command.payload.structure.distributionBoard,
floorId: null,
supplyType: null,
},
circuitList: command.payload.structure.circuitList,
sections: command.payload.structure.sections.map(
normalizeLegacySection
),
components: [],
};
}
if (
command.schemaVersion ===
previousDistributionBoardStructureCommandSchemaVersion
) {
return {
...command.payload.structure,
sections: command.payload.structure.sections.map(
normalizeLegacySection
),
components: [],
};
}
return command.payload.structure;
}
@@ -340,26 +228,10 @@ export function invertDistributionBoardStructureProjectCommand(
} as DistributionBoardStructureProjectCommand;
}
function normalizeLegacySection(
section: LegacyCircuitSectionSnapshot
): NormalizedDistributionBoardStructureSnapshot["sections"][number] {
const category =
section.key === "lighting" ||
section.key === "single_phase" ||
section.key === "three_phase"
? section.key
: null;
return {
...section,
category,
groupNumber: category === null ? null : 1,
};
}
export function assertDistributionBoardStructureSnapshot(
structure: unknown
): asserts structure is DistributionBoardStructureSnapshot {
assertDistributionBoardStructureSnapshotVersion(structure, "current");
assertDistributionBoardStructureSnapshotVersion(structure);
}
function assertDistributionBoardStructureProjectCommand(
@@ -375,63 +247,36 @@ function assertDistributionBoardStructureProjectCommand(
) {
throw new Error("Unsupported distribution-board structure command.");
}
if (
command.schemaVersion ===
legacyDistributionBoardStructureCommandSchemaVersion
) {
assertDistributionBoardStructureSnapshotVersion(
command.payload.structure,
"legacy"
);
return;
}
if (
command.schemaVersion ===
previousDistributionBoardStructureCommandSchemaVersion
) {
assertDistributionBoardStructureSnapshotVersion(
command.payload.structure,
"previous"
);
return;
}
if (
command.schemaVersion !==
distributionBoardStructureCommandSchemaVersion
) {
throw new Error("Unsupported distribution-board structure command.");
}
assertDistributionBoardStructureSnapshotVersion(
command.payload.structure,
"current"
);
assertDistributionBoardStructureSnapshotVersion(command.payload.structure);
}
function assertDistributionBoardStructureSnapshotVersion(
structure: unknown,
version: "legacy" | "previous" | "current"
structure: unknown
) {
const current = version === "current";
if (
!isPlainObject(structure) ||
Object.keys(structure).length !== (current ? 4 : 3)
Object.keys(structure).length !== 4
) {
throw new Error("Distribution-board structure is invalid.");
}
const { distributionBoard, circuitList, sections, components } =
structure;
const expectedSections = current
? defaultCircuitSectionDefinitions
: legacyDefaultCircuitSectionDefinitions;
const expectedSections = defaultCircuitSectionDefinitions;
if (
!isPlainObject(distributionBoard) ||
Object.keys(distributionBoard).length !==
(version === "legacy" ? 3 : 5) ||
Object.keys(distributionBoard).length !== 5 ||
!isPlainObject(circuitList) ||
Object.keys(circuitList).length !== 4 ||
!Array.isArray(sections) ||
sections.length !== expectedSections.length ||
(current && (!Array.isArray(components) || components.length !== 2))
!Array.isArray(components) ||
components.length !== 2
) {
throw new Error("Distribution-board structure is incomplete.");
}
@@ -441,10 +286,8 @@ function assertDistributionBoardStructureSnapshotVersion(
`distributionBoard.${field}`
);
}
if (version !== "legacy") {
assertNullableId(distributionBoard.floorId, "distributionBoard.floorId");
assertNullableSupplyType(distributionBoard.supplyType);
}
assertNullableId(distributionBoard.floorId, "distributionBoard.floorId");
assertNullableSupplyType(distributionBoard.supplyType);
for (const [field, value] of Object.entries(circuitList)) {
assertNonEmptyString(value, `circuitList.${field}`);
}
@@ -462,7 +305,7 @@ function assertDistributionBoardStructureSnapshotVersion(
const expected = expectedSections[index];
if (
!isPlainObject(section) ||
Object.keys(section).length !== (current ? 8 : 6)
Object.keys(section).length !== 8
) {
throw new Error("Default circuit section is invalid.");
}
@@ -477,10 +320,8 @@ function assertDistributionBoardStructureSnapshotVersion(
section.displayName !== expected.displayName ||
section.prefix !== expected.prefix ||
section.sortOrder !== expected.sortOrder ||
(current &&
(!("category" in expected) ||
section.category !== expected.category ||
section.groupNumber !== expected.groupNumber))
section.category !== expected.category ||
section.groupNumber !== expected.groupNumber
) {
throw new Error(
"Distribution-board structure has invalid default sections."
@@ -488,9 +329,7 @@ function assertDistributionBoardStructureSnapshotVersion(
}
}
if (current) {
assertDefaultComponents(components, circuitList.id);
}
assertDefaultComponents(components, circuitList.id);
}
function assertDefaultComponents(
@@ -156,7 +156,6 @@ export function cloneDistributionBoardSubtree(
...row,
id: createId(),
circuitId,
legacyConsumerId: null,
})),
protectionDevice:
circuit.protectionDevice === null ||
@@ -6,54 +6,29 @@ import {
export const projectSettingsUpdateCommandType =
"project.update-settings" as const;
export const legacyProjectSettingsUpdateCommandSchemaVersion = 1 as const;
export const previousProjectSettingsUpdateCommandSchemaVersion = 2 as const;
export const projectSettingsUpdateCommandSchemaVersion = 3 as const;
export const projectSettingsUpdateCommandSchemaVersion = 1 as const;
export interface LegacyProjectSettingsValues {
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
}
export interface PreviousProjectSettingsValues extends LegacyProjectSettingsValues {
export interface ProjectSettingsValues {
name: string;
internalProjectNumber: string | null;
externalProjectNumber: string | null;
buildingOwner: string | null;
description: string | null;
}
export interface ProjectSettingsValues extends PreviousProjectSettingsValues {
singlePhaseVoltageV: number;
threePhaseVoltageV: number;
enabledDistributionBoardSupplyTypes: DistributionBoardSupplyType[];
}
export interface LegacyProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<LegacyProjectSettingsValues> {
schemaVersion: typeof legacyProjectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export interface CurrentProjectSettingsUpdateProjectCommand
export interface ProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<ProjectSettingsValues> {
schemaVersion: typeof projectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export interface PreviousProjectSettingsUpdateProjectCommand
extends SerializedProjectCommand<PreviousProjectSettingsValues> {
schemaVersion: typeof previousProjectSettingsUpdateCommandSchemaVersion;
type: typeof projectSettingsUpdateCommandType;
}
export type ProjectSettingsUpdateProjectCommand =
| LegacyProjectSettingsUpdateProjectCommand
| PreviousProjectSettingsUpdateProjectCommand
| CurrentProjectSettingsUpdateProjectCommand;
export function createProjectSettingsUpdateProjectCommand(
values: ProjectSettingsValues
): CurrentProjectSettingsUpdateProjectCommand {
const command: CurrentProjectSettingsUpdateProjectCommand = {
): ProjectSettingsUpdateProjectCommand {
const command: ProjectSettingsUpdateProjectCommand = {
schemaVersion: projectSettingsUpdateCommandSchemaVersion,
type: projectSettingsUpdateCommandType,
payload: { ...values },
@@ -67,11 +42,7 @@ export function assertProjectSettingsUpdateProjectCommand(
): asserts command is ProjectSettingsUpdateProjectCommand {
if (
command.type !== projectSettingsUpdateCommandType ||
(command.schemaVersion !==
legacyProjectSettingsUpdateCommandSchemaVersion &&
command.schemaVersion !==
previousProjectSettingsUpdateCommandSchemaVersion &&
command.schemaVersion !== projectSettingsUpdateCommandSchemaVersion)
command.schemaVersion !== projectSettingsUpdateCommandSchemaVersion
) {
throw new Error("Unsupported project settings update command.");
}
@@ -80,14 +51,6 @@ export function assertProjectSettingsUpdateProjectCommand(
"Project settings update command contains invalid values."
);
}
if (command.schemaVersion === legacyProjectSettingsUpdateCommandSchemaVersion) {
assertLegacyValues(command.payload);
return;
}
const expectedKeyCount =
command.schemaVersion === previousProjectSettingsUpdateCommandSchemaVersion
? 7
: 8;
if (
!isTrimmedString(command.payload.name, 1, 200) ||
!isNullableTrimmedString(command.payload.internalProjectNumber, 100) ||
@@ -96,14 +59,13 @@ export function assertProjectSettingsUpdateProjectCommand(
!isNullableTrimmedString(command.payload.description, 2000) ||
!isPositiveFiniteNumber(command.payload.singlePhaseVoltageV) ||
!isPositiveFiniteNumber(command.payload.threePhaseVoltageV) ||
Object.keys(command.payload).length !== expectedKeyCount
Object.keys(command.payload).length !== 8
) {
throw new Error(
"Project settings update command contains invalid values."
);
}
if (
command.schemaVersion === projectSettingsUpdateCommandSchemaVersion &&
!isValidSupplyTypes(
command.payload.enabledDistributionBoardSupplyTypes
)
@@ -114,28 +76,6 @@ export function assertProjectSettingsUpdateProjectCommand(
}
}
export function normalizeProjectSettingsValues(
command: ProjectSettingsUpdateProjectCommand,
current: ProjectSettingsValues
): ProjectSettingsValues {
if (command.schemaVersion === legacyProjectSettingsUpdateCommandSchemaVersion) {
return {
...current,
...command.payload,
};
}
if (
command.schemaVersion === previousProjectSettingsUpdateCommandSchemaVersion
) {
return {
...command.payload,
enabledDistributionBoardSupplyTypes:
current.enabledDistributionBoardSupplyTypes,
};
}
return command.payload;
}
function isValidSupplyTypes(
value: unknown
): value is DistributionBoardSupplyType[] {
@@ -153,20 +93,6 @@ function isValidSupplyTypes(
);
}
function assertLegacyValues(
payload: Record<string, unknown>
): asserts payload is Record<string, unknown> & LegacyProjectSettingsValues {
if (
!isPositiveFiniteNumber(payload.singlePhaseVoltageV) ||
!isPositiveFiniteNumber(payload.threePhaseVoltageV) ||
Object.keys(payload).length !== 2
) {
throw new Error(
"Project settings update command contains invalid voltages."
);
}
}
function isPositiveFiniteNumber(value: unknown): value is number {
return (
typeof value === "number" &&
+24 -304
View File
@@ -1,11 +1,7 @@
import { z } from "zod";
import {
defaultDistributionBoardSupplyTypes,
distributionBoardSupplyTypes,
} from "../../shared/constants/distribution-board.js";
import { distributionBoardSupplyTypes } from "../../shared/constants/distribution-board.js";
import {
circuitGroupCategories,
type CircuitGroupCategory,
} from "../../shared/constants/circuit-group.js";
import {
distributionBoardComponentPlacements,
@@ -21,61 +17,38 @@ import {
import {
resolveCircuitPhaseType,
resolveProjectVoltage,
normalizeKnownElectricalPhaseType,
} from "../services/project-voltage.service.js";
export const legacyProjectStateSnapshotSchemaVersion = 1 as const;
export const previousProjectStateSnapshotSchemaVersion = 2 as const;
export const distributionBoardProjectStateSnapshotSchemaVersion = 3 as const;
export const supplyTypesProjectStateSnapshotSchemaVersion = 4 as const;
export const voltageProjectStateSnapshotSchemaVersion = 5 as const;
export const simultaneityFactorProjectStateSnapshotSchemaVersion = 6 as const;
export const projectStateSnapshotSchemaVersion = 7 as const;
export const projectStateSnapshotSchemaVersion = 1 as const;
const idSchema = z.string().trim().min(1);
const nullableStringSchema = z.string().nullable();
const finiteNumberSchema = z.number().finite();
const legacyProjectSchema = z
const projectSchema = z
.object({
id: idSchema,
name: z.string().trim().min(1),
internalProjectNumber: z.string().trim().min(1).max(100).nullable(),
externalProjectNumber: z.string().trim().min(1).max(100).nullable(),
buildingOwner: z.string().trim().min(1).max(200).nullable(),
description: z.string().trim().min(1).max(2000).nullable(),
singlePhaseVoltageV: finiteNumberSchema.positive(),
threePhaseVoltageV: finiteNumberSchema.positive(),
enabledDistributionBoardSupplyTypes: z
.array(z.enum(distributionBoardSupplyTypes))
.min(1)
.refine((values) => new Set(values).size === values.length),
})
.strict();
const projectSchema = legacyProjectSchema.extend({
internalProjectNumber: z.string().trim().min(1).max(100).nullable(),
externalProjectNumber: z.string().trim().min(1).max(100).nullable(),
buildingOwner: z.string().trim().min(1).max(200).nullable(),
description: z.string().trim().min(1).max(2000).nullable(),
});
const currentProjectSchema = projectSchema.extend({
enabledDistributionBoardSupplyTypes: z
.array(z.enum(distributionBoardSupplyTypes))
.min(1)
.refine((values) => new Set(values).size === values.length),
});
const legacyDistributionBoardSchema = z
const distributionBoardSchema = z
.object({
id: idSchema,
projectId: idSchema,
name: z.string().trim().min(1),
})
.strict();
const distributionBoardSchema = legacyDistributionBoardSchema
.extend({
floorId: idSchema.nullable(),
supplyType: z.enum(distributionBoardSupplyTypes).nullable(),
})
.strict();
const currentDistributionBoardSchema = distributionBoardSchema
.extend({
simultaneityFactor: finiteNumberSchema.min(0).max(1),
})
.strict();
@@ -89,7 +62,7 @@ const circuitListSchema = z
})
.strict();
const legacyCircuitSectionSchema = z
const circuitSectionSchema = z
.object({
id: idSchema,
circuitListId: idSchema,
@@ -97,11 +70,6 @@ const legacyCircuitSectionSchema = z
displayName: z.string().trim().min(1),
prefix: z.string().trim().min(1),
sortOrder: finiteNumberSchema,
})
.strict();
const circuitSectionSchema = legacyCircuitSectionSchema
.extend({
category: z.enum(circuitGroupCategories).nullable(),
groupNumber: z.number().int().positive().nullable(),
})
@@ -121,11 +89,10 @@ const circuitDeviceRowSchema = z
id: idSchema,
circuitId: idSchema,
linkedProjectDeviceId: idSchema.nullable(),
legacyConsumerId: z.string().nullable(),
sortOrder: finiteNumberSchema,
name: z.string().trim().min(1),
displayName: z.string().trim().min(1),
phaseType: nullableStringSchema,
phaseType: z.enum(["single_phase", "three_phase"]).nullable(),
connectionKind: nullableStringSchema,
costGroup: nullableStringSchema,
category: nullableStringSchema,
@@ -150,9 +117,6 @@ const circuitSchema = z
equipmentIdentifier: z.string().trim().min(1),
displayName: nullableStringSchema,
sortOrder: finiteNumberSchema,
protectionType: nullableStringSchema,
protectionRatedCurrent: finiteNumberSchema.nonnegative().nullable(),
protectionCharacteristic: nullableStringSchema,
cableType: nullableStringSchema,
cableCrossSection: nullableStringSchema,
cableLength: finiteNumberSchema.nonnegative().nullable(),
@@ -280,18 +244,13 @@ const componentProtectionDeviceSchema = z
.strict()
.superRefine(validatePersistedProtectionDevice);
const legacyProjectStateSnapshotContents = {
const projectStateSnapshotContents = {
circuitLists: z.array(circuitListSchema),
circuitSections: z.array(legacyCircuitSectionSchema),
circuitSections: z.array(circuitSectionSchema),
circuits: z.array(circuitSchema),
projectDevices: z.array(projectDeviceSchema),
floors: z.array(floorSchema),
rooms: z.array(roomSchema),
};
const currentProjectStateSnapshotContents = {
...legacyProjectStateSnapshotContents,
circuitSections: z.array(circuitSectionSchema),
distributionBoardComponents: z.array(distributionBoardComponentSchema),
circuitProtectionDevices: z.array(circuitProtectionDeviceSchema),
distributionBoardComponentProtectionDevices: z.array(
@@ -299,64 +258,14 @@ const currentProjectStateSnapshotContents = {
),
};
const legacyProjectStateSnapshotSchema = z
export const projectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(legacyProjectStateSnapshotSchemaVersion),
project: legacyProjectSchema,
distributionBoards: z.array(legacyDistributionBoardSchema),
...legacyProjectStateSnapshotContents,
})
.strict();
const previousProjectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(previousProjectStateSnapshotSchemaVersion),
project: projectSchema,
distributionBoards: z.array(legacyDistributionBoardSchema),
...legacyProjectStateSnapshotContents,
})
.strict();
const distributionBoardProjectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(
distributionBoardProjectStateSnapshotSchemaVersion
),
project: projectSchema,
distributionBoards: z.array(distributionBoardSchema),
...legacyProjectStateSnapshotContents,
})
.strict();
const supplyTypesProjectStateSnapshotSchema = z
.object({
schemaVersion: z.literal(supplyTypesProjectStateSnapshotSchemaVersion),
project: currentProjectSchema,
distributionBoards: z.array(distributionBoardSchema),
...legacyProjectStateSnapshotContents,
})
.strict();
const voltageProjectStateSnapshotSchema =
supplyTypesProjectStateSnapshotSchema.extend({
schemaVersion: z.literal(
voltageProjectStateSnapshotSchemaVersion
),
});
const simultaneityFactorProjectStateSnapshotSchema =
voltageProjectStateSnapshotSchema.extend({
schemaVersion: z.literal(
simultaneityFactorProjectStateSnapshotSchemaVersion
),
distributionBoards: z.array(currentDistributionBoardSchema),
});
export const projectStateSnapshotSchema =
simultaneityFactorProjectStateSnapshotSchema.extend({
schemaVersion: z.literal(projectStateSnapshotSchemaVersion),
...currentProjectStateSnapshotContents,
});
project: projectSchema,
distributionBoards: z.array(distributionBoardSchema),
...projectStateSnapshotContents,
})
.strict();
export type ProjectStateSnapshot = z.infer<
typeof projectStateSnapshotSchema
@@ -365,68 +274,7 @@ export type ProjectStateSnapshot = z.infer<
export function parseProjectStateSnapshot(
value: unknown
): ProjectStateSnapshot {
const version = isPlainObject(value) ? value.schemaVersion : undefined;
const parsedSnapshot =
version === legacyProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
upgradePreviousProjectStateSnapshot(
upgradeLegacyProjectStateSnapshot(
legacyProjectStateSnapshotSchema.parse(value)
)
)
)
)
)
)
: version === previousProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
upgradePreviousProjectStateSnapshot(
previousProjectStateSnapshotSchema.parse(value)
)
)
)
)
)
: version === distributionBoardProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
upgradeDistributionBoardProjectStateSnapshot(
distributionBoardProjectStateSnapshotSchema.parse(
value
)
)
)
)
)
: version === supplyTypesProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
upgradeSupplyTypesProjectStateSnapshot(
supplyTypesProjectStateSnapshotSchema.parse(value)
)
)
)
: version === voltageProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
upgradeVoltageProjectStateSnapshot(
voltageProjectStateSnapshotSchema.parse(value)
)
)
: version ===
simultaneityFactorProjectStateSnapshotSchemaVersion
? upgradeSimultaneityFactorProjectStateSnapshot(
simultaneityFactorProjectStateSnapshotSchema.parse(
value
)
)
: projectStateSnapshotSchema.parse(value);
const parsedSnapshot = projectStateSnapshotSchema.parse(value);
const snapshot = normalizeSnapshotPhaseTypes(parsedSnapshot);
assertProjectStateSnapshotRelations(snapshot);
return snapshot;
@@ -446,15 +294,13 @@ function normalizeSnapshotPhaseTypes(
circuits: snapshot.circuits.map((circuit) => {
const section = sectionById.get(circuit.sectionId);
const deviceRows = circuit.deviceRows.map((row) => {
const normalizedPhaseType =
normalizeKnownElectricalPhaseType(row.phaseType);
const linkedPhaseType = row.linkedProjectDeviceId
? projectDeviceById.get(row.linkedProjectDeviceId)?.phaseType
: undefined;
return {
...row,
phaseType:
normalizedPhaseType ??
row.phaseType ??
linkedPhaseType ??
(section?.key === "three_phase"
? "three_phase"
@@ -478,132 +324,6 @@ function normalizeSnapshotPhaseTypes(
};
}
function upgradeLegacyProjectStateSnapshot(
snapshot: z.infer<typeof legacyProjectStateSnapshotSchema>
): z.infer<typeof previousProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: previousProjectStateSnapshotSchemaVersion,
project: {
...snapshot.project,
internalProjectNumber: null,
externalProjectNumber: null,
buildingOwner: null,
description: null,
},
};
}
function upgradePreviousProjectStateSnapshot(
snapshot: z.infer<typeof previousProjectStateSnapshotSchema>
): z.infer<typeof distributionBoardProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: distributionBoardProjectStateSnapshotSchemaVersion,
distributionBoards: snapshot.distributionBoards.map((board) => ({
...board,
floorId: null,
supplyType: null,
})),
};
}
function upgradeDistributionBoardProjectStateSnapshot(
snapshot: z.infer<typeof distributionBoardProjectStateSnapshotSchema>
): z.infer<typeof supplyTypesProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: supplyTypesProjectStateSnapshotSchemaVersion,
project: {
...snapshot.project,
enabledDistributionBoardSupplyTypes: [
...defaultDistributionBoardSupplyTypes,
],
},
};
}
function upgradeSupplyTypesProjectStateSnapshot(
snapshot: z.infer<typeof supplyTypesProjectStateSnapshotSchema>
): z.infer<typeof voltageProjectStateSnapshotSchema> {
const settings = snapshot.project;
const sectionById = new Map(
snapshot.circuitSections.map((section) => [section.id, section])
);
return {
...snapshot,
schemaVersion: voltageProjectStateSnapshotSchemaVersion,
projectDevices: snapshot.projectDevices.map((device) => ({
...device,
voltageV: resolveProjectVoltage(device.phaseType, settings),
})),
circuits: snapshot.circuits.map((circuit) => {
const section = sectionById.get(circuit.sectionId);
if (!section) {
return circuit;
}
const phaseType = resolveCircuitPhaseType(
section.key,
circuit.deviceRows.map((row) => row.phaseType)
);
return {
...circuit,
voltage: resolveProjectVoltage(phaseType, settings),
};
}),
};
}
function upgradeVoltageProjectStateSnapshot(
snapshot: z.infer<typeof voltageProjectStateSnapshotSchema>
): z.infer<typeof simultaneityFactorProjectStateSnapshotSchema> {
return {
...snapshot,
schemaVersion: simultaneityFactorProjectStateSnapshotSchemaVersion,
distributionBoards: snapshot.distributionBoards.map((board) => ({
...board,
simultaneityFactor: 1,
})),
};
}
function upgradeSimultaneityFactorProjectStateSnapshot(
snapshot: z.infer<typeof simultaneityFactorProjectStateSnapshotSchema>
): ProjectStateSnapshot {
return {
...snapshot,
schemaVersion: projectStateSnapshotSchemaVersion,
circuitSections: snapshot.circuitSections.map((section) => {
const category = initialCategoryBySectionKey(section.key);
return {
...section,
category,
groupNumber: category === null ? null : 1,
};
}),
distributionBoardComponents: [],
circuitProtectionDevices: [],
distributionBoardComponentProtectionDevices: [],
};
}
function initialCategoryBySectionKey(
key: string
): CircuitGroupCategory | null {
if (
key === "lighting" ||
key === "single_phase" ||
key === "three_phase"
) {
return key;
}
return null;
}
function isPlainObject(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
export function serializeProjectStateSnapshot(
snapshot: ProjectStateSnapshot
): string {
@@ -156,7 +156,6 @@ export function remapProjectState(
row.linkedProjectDeviceId === null
? null
: requiredId(deviceIds, row.linkedProjectDeviceId),
legacyConsumerId: null,
roomId:
row.roomId === null ? null : requiredId(roomIds, row.roomId),
})),