export type EngineeringKind = 'Assembly' | 'BIM' | 'Mesh' | 'Surface'
export type EngineeringArtifact = { id: string; kind: EngineeringKind; sourceRefs: string[]; payload: Record<string, unknown>; version: number }
export type EngineeringIntegrity = { complete: boolean; kinds: EngineeringKind[]; missingKinds: EngineeringKind[]; missingRefs: string[]; artifacts: number }
export type EngineeringSnapshot = { id: string; label: string; artifacts: EngineeringArtifact[]; version: number }
export type EngineeringProjectApi = { add(input: Omit<EngineeringArtifact, 'version'> & { version?: number }): EngineeringArtifact; update(id: string, patch: Partial<Pick<EngineeringArtifact, 'sourceRefs' | 'payload'>>): EngineeringArtifact; integrity(): EngineeringIntegrity; snapshot(): EngineeringSnapshot; save(): string; load(serialized: string): EngineeringSnapshot }
const kinds: EngineeringKind[] = ['Assembly', 'BIM', 'Mesh', 'Surface']
const clone = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T
export const createEngineeringProject = (id = 'engineering', label = 'Engineering project'): EngineeringProjectApi => { const artifacts = new Map<string, EngineeringArtifact>(); let version = 0; const add = (input: Omit<EngineeringArtifact, 'version'> & { version?: number }) => { if (!input.id.trim() || artifacts.has(input.id)) throw new RangeError(`Engineering artifact already exists: ${input.id}`); if (!kinds.includes(input.kind)) throw new RangeError(`Unsupported engineering artifact kind: ${input.kind}`); const artifact: EngineeringArtifact = { id: input.id, kind: input.kind, sourceRefs: [...new Set(input.sourceRefs)], payload: clone(input.payload), version: input.version ?? 1 }; artifacts.set(artifact.id, artifact); version += 1; return clone(artifact) }; const update = (artifactId: string, patch: Partial<Pick<EngineeringArtifact, 'sourceRefs' | 'payload'>>) => { const artifact = artifacts.get(artifactId); if (!artifact) throw new RangeError(`Engineering artifact does not exist: ${artifactId}`); if (patch.sourceRefs) artifact.sourceRefs = [...new Set(patch.sourceRefs)]; if (patch.payload) artifact.payload = clone(patch.payload); artifact.version += 1; version += 1; return clone(artifact) }; const integrity = (): EngineeringIntegrity => { const present = [...new Set([...artifacts.values()].map((artifact) => artifact.kind))]; const missingKinds = kinds.filter((kind) => !present.includes(kind)); const missingRefs = [...new Set([...artifacts.values()].flatMap((artifact) => artifact.sourceRefs.filter((ref) => !artifacts.has(ref)).map((ref) => `${artifact.id}->${ref}`)))].sort(); return { complete: missingKinds.length === 0 && missingRefs.length === 0, kinds: present.sort(), missingKinds, missingRefs, artifacts: artifacts.size } }; const snapshot = (): EngineeringSnapshot => ({ id, label, artifacts: [...artifacts.values()].map(clone), version }); const save = () => `${JSON.stringify(snapshot(), null, 2)}\n`; const load = (serialized: string) => { let parsed: unknown; try { parsed = JSON.parse(serialized) } catch { throw new Error('Engineering project is not valid JSON.') }; if (!parsed || typeof parsed !== 'object' || !Array.isArray((parsed as { artifacts?: unknown }).artifacts)) throw new Error('Engineering project artifacts are missing.'); const source = parsed as EngineeringSnapshot; artifacts.clear(); for (const artifact of source.artifacts) add(artifact); version = source.version; return snapshot() }; return { add, update, integrity, snapshot, save, load } }
