// The canonical ModelOp alphabet — every mutation to a SysMLModel is // expressible as one or more of these. See docs/sync.md §3 for the design. // // `tempId` lets clients optimistically create elements with a client-generated // id; applyOps may rewrite to a canonical id and return the mapping. (M5 // in-memory: tempId IS the canonical id; the rewrite seam is here for the // later DB / SSE iteration.) import type { Block, BlockKind, Association, AssociationKind, Constraint, Property, PropertyType, Multiplicity, Requirement, RequirementRelation, } from "../sysml/model"; // ─── Block ops ─────────────────────────────────────────────────────────── export interface AddBlockOp { kind: "add-block"; block: Block; tempId?: string; } export interface UpdateBlockOp { kind: "update-block"; blockId: string; patch: Partial>; } export interface RemoveBlockOp { kind: "remove-block"; blockId: string; } // ─── Property ops ──────────────────────────────────────────────────────── export interface AddPropertyOp { kind: "add-property"; blockId: string; property: Property; tempId?: string; } export interface UpdatePropertyOp { kind: "update-property"; blockId: string; propertyId: string; patch: Partial>; } export interface RemovePropertyOp { kind: "remove-property"; blockId: string; propertyId: string; } // ─── Association ops ───────────────────────────────────────────────────── export interface AddAssociationOp { kind: "add-association"; association: Association; tempId?: string; } export interface UpdateAssociationOp { kind: "update-association"; associationId: string; patch: Partial>; } export interface RemoveAssociationOp { kind: "remove-association"; associationId: string; } // ─── Constraint ops ────────────────────────────────────────────────────── export interface AddConstraintOp { kind: "add-constraint"; constraint: Constraint; tempId?: string; } export interface UpdateConstraintOp { kind: "update-constraint"; constraintId: string; patch: Partial>; } export interface RemoveConstraintOp { kind: "remove-constraint"; constraintId: string; } // ─── Requirement ops ───────────────────────────────────────────────────── export interface AddRequirementOp { kind: "add-requirement"; requirement: Requirement; tempId?: string; } export interface UpdateRequirementOp { kind: "update-requirement"; requirementId: string; patch: Partial>; } export interface RemoveRequirementOp { kind: "remove-requirement"; requirementId: string; } export interface AddRelationOp { kind: "add-relation"; requirementId: string; relation: RequirementRelation; } export interface RemoveRelationOp { kind: "remove-relation"; requirementId: string; relationIndex: number; } // ─── Union ─────────────────────────────────────────────────────────────── export type ModelOp = | AddBlockOp | UpdateBlockOp | RemoveBlockOp | AddPropertyOp | UpdatePropertyOp | RemovePropertyOp | AddAssociationOp | UpdateAssociationOp | RemoveAssociationOp | AddConstraintOp | UpdateConstraintOp | RemoveConstraintOp | AddRequirementOp | UpdateRequirementOp | RemoveRequirementOp | AddRelationOp | RemoveRelationOp; // ─── Op constructors (call sites stay readable) ───────────────────────── export function addBlock(block: Block, tempId?: string): AddBlockOp { return { kind: "add-block", block, tempId }; } export function updateBlock(blockId: string, patch: UpdateBlockOp["patch"]): UpdateBlockOp { return { kind: "update-block", blockId, patch }; } export function removeBlock(blockId: string): RemoveBlockOp { return { kind: "remove-block", blockId }; } export function addProperty(blockId: string, property: Property, tempId?: string): AddPropertyOp { return { kind: "add-property", blockId, property, tempId }; } export function updateProperty(blockId: string, propertyId: string, patch: UpdatePropertyOp["patch"]): UpdatePropertyOp { return { kind: "update-property", blockId, propertyId, patch }; } export function removeProperty(blockId: string, propertyId: string): RemovePropertyOp { return { kind: "remove-property", blockId, propertyId }; } export function addAssociation(association: Association, tempId?: string): AddAssociationOp { return { kind: "add-association", association, tempId }; } export function updateAssociation(associationId: string, patch: UpdateAssociationOp["patch"]): UpdateAssociationOp { return { kind: "update-association", associationId, patch }; } export function removeAssociation(associationId: string): RemoveAssociationOp { return { kind: "remove-association", associationId }; } export function addConstraint(constraint: Constraint, tempId?: string): AddConstraintOp { return { kind: "add-constraint", constraint, tempId }; } export function updateConstraint(constraintId: string, patch: UpdateConstraintOp["patch"]): UpdateConstraintOp { return { kind: "update-constraint", constraintId, patch }; } export function removeConstraint(constraintId: string): RemoveConstraintOp { return { kind: "remove-constraint", constraintId }; } export function addRequirement(requirement: Requirement, tempId?: string): AddRequirementOp { return { kind: "add-requirement", requirement, tempId }; } export function updateRequirement(requirementId: string, patch: UpdateRequirementOp["patch"]): UpdateRequirementOp { return { kind: "update-requirement", requirementId, patch }; } export function removeRequirement(requirementId: string): RemoveRequirementOp { return { kind: "remove-requirement", requirementId }; } export function addRelation(requirementId: string, relation: RequirementRelation): AddRelationOp { return { kind: "add-relation", requirementId, relation }; } export function removeRelation(requirementId: string, relationIndex: number): RemoveRelationOp { return { kind: "remove-relation", requirementId, relationIndex }; } // ─── Helpers ───────────────────────────────────────────────────────────── let counter = 0; /** Generate a temp id. Distinct prefix so applyOps can recognize them. */ export function tempId(prefix = "tmp"): string { counter++; return `${prefix}_${Date.now().toString(36)}_${counter}`; } /** Quick property factory used by clients producing add-property ops. */ export function newProperty(name: string, type: PropertyType = { kind: "string" }, multiplicity: Multiplicity = "0..1"): Property { return { id: tempId("p"), name, type, multiplicity }; }