Add per-project socket limit and Revit socket device-type flag

- Project setting maxSocketsPerCircuit (migration 0018,
  project.update-settings schema 4, snapshot schema 11); older command
  and snapshot versions keep or upgrade the value to null.
- Revit configuration schema 2 marks device types as socket; version 1
  stays readable and derives the flag from socketCount family rules.
- Settings dialog field and device-type card on the Revit page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Julian Appel 2026-10-30 21:18:08 +01:00
parent 85cbdbf8e0
commit e48042bde3
35 changed files with 3781 additions and 41 deletions

View file

@ -13,10 +13,19 @@ import { createDefaultExternalCsvConfiguration } from "../src/external-model/csv
import { externalCsvTestColumns } from "./fixtures/revit-csv-fixtures.js";
import { zodIssues } from "./support/zod-assertions.js";
/** Removes the fields introduced by snapshot schema version 9. */
/** Removes the field introduced by snapshot schema version 11. */
function withoutVersionElevenFields<T extends { project: object }>(
snapshot: T
): T {
delete (snapshot.project as Record<string, unknown>).maxSocketsPerCircuit;
return snapshot;
}
/** Removes the fields introduced by snapshot schema versions 9 to 11. */
function withoutVersionNineFields<
T extends { distributionBoards: object[] },
T extends { project: object; distributionBoards: object[] },
>(snapshot: T): T {
withoutVersionElevenFields(snapshot);
const record = snapshot as Record<string, unknown>;
record.externalCsvConfiguration = null;
delete record.externalModelConfiguration;
@ -29,7 +38,7 @@ function withoutVersionNineFields<
/** Removes the fields introduced by snapshot schema version 7. */
function withoutVersionSevenFields<
T extends { floors: object[]; distributionBoards: object[] },
T extends { project: object; floors: object[]; distributionBoards: object[] },
>(snapshot: T): T {
withoutVersionNineFields(snapshot);
delete (snapshot as Record<string, unknown>).buildings;
@ -82,6 +91,7 @@ function minimalSnapshot() {
workStage: "LPH 5" as string | null,
projectLead: "P. Leitung" as string | null,
projectEditor: null as string | null,
maxSocketsPerCircuit: null as number | null,
},
distributionBoards: [],
externalModelConfiguration: null,
@ -351,6 +361,24 @@ describe("project state snapshot model", () => {
});
});
it("keeps the socket limit in version 11 and upgrades version 10 without a limit", () => {
const current = structuredClone(minimalSnapshot());
current.project.maxSocketsPerCircuit = 8;
assert.equal(parseProjectStateSnapshot(current).project.maxSocketsPerCircuit, 8);
const previous = withoutVersionElevenFields(structuredClone(minimalSnapshot()));
previous.schemaVersion = 10 as typeof previous.schemaVersion;
const upgraded = parseProjectStateSnapshot(previous);
assert.equal(upgraded.schemaVersion, projectStateSnapshotSchemaVersion);
assert.equal(upgraded.project.maxSocketsPerCircuit, null);
const invalid = structuredClone(minimalSnapshot());
invalid.project.maxSocketsPerCircuit = 0;
assert.throws(() => parseProjectStateSnapshot(invalid));
const missing = withoutVersionElevenFields(structuredClone(minimalSnapshot()));
assert.throws(() => parseProjectStateSnapshot(missing));
});
it("upgrades snapshot version 3 with an empty external model", () => {
const previous = withoutVersionSixFields(structuredClone(minimalSnapshot()));
previous.schemaVersion = 3 as typeof previous.schemaVersion;