sysml-diagram 0.38.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/out/main.js CHANGED
@@ -161060,6 +161060,9 @@ var REFERENCE_KEYWORDS = {
161060
161060
  RequireConstraintStmt: "require",
161061
161061
  AssumeConstraintStmt: "assume"
161062
161062
  };
161063
+ function isInlineConstraintClaim(node) {
161064
+ return REFERENCE_KEYWORDS[node.$type] !== void 0 && node.constraintKw === true;
161065
+ }
161063
161066
  function constraintReferenceOf(node) {
161064
161067
  const kind = REFERENCE_KEYWORDS[node.$type];
161065
161068
  if (!kind)
@@ -164101,6 +164104,28 @@ var FeaturePathResolver = class {
164101
164104
  }
164102
164105
  };
164103
164106
 
164107
+ // ../language-server/out/src/services/calculation-semantics.js
164108
+ function effectiveCalculationResult(node, resolve8) {
164109
+ const seen = /* @__PURE__ */ new Set();
164110
+ const walk = (current2, depth) => {
164111
+ if (depth > 32 || seen.has(current2))
164112
+ return void 0;
164113
+ seen.add(current2);
164114
+ const parameter = current2.members?.find((member) => member.$type === "ReturnDecl");
164115
+ if (parameter || current2.body)
164116
+ return {
164117
+ parameter,
164118
+ expression: parameter?.value ?? current2.body,
164119
+ inherited: current2 !== node
164120
+ };
164121
+ const parents = supertypesOf(current2, resolve8).filter((parent) => parent.$type === "CalcDecl");
164122
+ if (parents.length !== 1)
164123
+ return void 0;
164124
+ return walk(parents[0], depth + 1);
164125
+ };
164126
+ return walk(node, 0);
164127
+ }
164128
+
164104
164129
  // ../language-server/out/src/platform/platform.js
164105
164130
  var current;
164106
164131
  function setPlatform(platform) {
@@ -166671,6 +166696,7 @@ var isBareDefDecl = (node) => node.$type === "BareDefDecl";
166671
166696
  var isDefinitionNode = (node) => isDefinitionKind(node) && node.isDef === true || isBareDefDecl(node);
166672
166697
  var ACTION_FLOW_STEP_TYPES = /* @__PURE__ */ new Set([
166673
166698
  "ActionDecl",
166699
+ "CalcDecl",
166674
166700
  "PerformStmt",
166675
166701
  "AssignNode",
166676
166702
  "SendNode",
@@ -167585,8 +167611,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167585
167611
  case "iv":
167586
167612
  return firstContent(isPartDecl) ?? firstContent(isPackage) ?? firstThat(isPartDecl) ?? firstThat(isPackage) ?? root4;
167587
167613
  case "afv":
167588
- return firstContent((n2) => isActionDecl(n2) || this.isCaseLike(n2)) ?? firstContent(isPartDecl) ?? // REQ-196 — action flow over a part's performers
167589
- firstThat((n2) => isActionDecl(n2) || this.isCaseLike(n2)) ?? root4;
167614
+ return firstContent((n2) => isActionDecl(n2) || isCalcDecl(n2) || this.isCaseLike(n2)) ?? firstContent(isPartDecl) ?? // REQ-196 — action flow over a part's performers
167615
+ firstThat((n2) => isActionDecl(n2) || isCalcDecl(n2) || this.isCaseLike(n2)) ?? root4;
167590
167616
  case "stv":
167591
167617
  return firstContent((n2) => isExhibitStateDecl(n2) || isStateDecl(n2)) ?? firstThat((n2) => isExhibitStateDecl(n2) || isStateDecl(n2)) ?? root4;
167592
167618
  case "sv":
@@ -167636,7 +167662,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167636
167662
  case "iv":
167637
167663
  return isPartDecl(node);
167638
167664
  case "afv":
167639
- return isActionDecl(node) || isPartDecl(node) || this.isCaseLike(node);
167665
+ return isActionDecl(node) || isCalcDecl(node) || isPartDecl(node) || this.isCaseLike(node);
167640
167666
  case "stv":
167641
167667
  return isStateDecl(node) || isExhibitStateDecl(node) || isPartDecl(node);
167642
167668
  case "sv":
@@ -167659,7 +167685,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167659
167685
  // and `findAnchor` drills to them, so a view opened on the OWNER of an empty
167660
167686
  // action definition shows the same thing the package overview does.
167661
167687
  isAfvScaffoldAnchor(node) {
167662
- return isActionDecl(node) && !!nameOf2(node) && (node.isDef === true || this.isDirectPackageMember(node));
167688
+ return (isActionDecl(node) || isCalcDecl(node)) && !!nameOf2(node) && (node.isDef === true || this.isDirectPackageMember(node));
167663
167689
  }
167664
167690
  // REQ-196 — a member that is a STEP of the flow its holder runs: an action
167665
167691
  // usage, a send/accept/assign node, or a `perform`. An action DEFINITION is
@@ -167669,7 +167695,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167669
167695
  // `hasAnchorContent` and `hasAfvOverviewDescendant` both read, so what makes a
167670
167696
  // part an anchor and what makes it a scope cannot drift apart.
167671
167697
  isAfvFlowNode(member, index2) {
167672
- return !this.isSwimlaneClaim(member, index2) && (isActionDecl(member) && member.isDef !== true || ["SendNode", "AcceptNode", "AssignNode", "PerformStmt"].includes(member.$type));
167698
+ return !this.isSwimlaneClaim(member, index2) && ((isActionDecl(member) || isCalcDecl(member)) && member.isDef !== true || ["SendNode", "AcceptNode", "AssignNode", "PerformStmt"].includes(member.$type));
167673
167699
  }
167674
167700
  // REQ-196 — does this part's own subtree hold action flow anywhere? Counted
167675
167701
  // over the nested part usages of a flow-less part, this is what tells a shared
@@ -167795,20 +167821,29 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167795
167821
  * `accept` payload in on top) and the Interconnection View, where an action
167796
167822
  * drawn inside a part must expose the same pins a flow can dock on. */
167797
167823
  actionPinPorts(action, ownerId, index2, uri) {
167798
- const inheritedFrom = action.isDef === true ? void 0 : this.resolveType(action, index2);
167799
167824
  const directed = /* @__PURE__ */ new Map();
167800
- for (const member of inheritedFrom ? membersOf(inheritedFrom) : []) {
167801
- if (nameOf2(member) && directionOf(member))
167802
- directed.set(nameOf2(member), member);
167825
+ const inheritedTypes = /* @__PURE__ */ new Map();
167826
+ for (const owner of [...this.inheritedFeatureOwnersOf(action, index2)].reverse()) {
167827
+ for (const member of membersOf(owner)) {
167828
+ const targets = redefinitionTargetsOf(member).map(lastSeg);
167829
+ for (const target of targets)
167830
+ directed.delete(target);
167831
+ if (effectiveNameOf(member) && directionOf(member)) {
167832
+ directed.set(effectiveNameOf(member), member);
167833
+ inheritedTypes.set(effectiveNameOf(member), typeText(member) ?? inheritedTypes.get(targets[0] ?? effectiveNameOf(member)));
167834
+ }
167835
+ }
167803
167836
  }
167804
167837
  for (const member of membersOf(action)) {
167805
- if (nameOf2(member) && directionOf(member))
167806
- directed.set(nameOf2(member), member);
167838
+ for (const target of redefinitionTargetsOf(member))
167839
+ directed.delete(lastSeg(target));
167840
+ if (effectiveNameOf(member) && directionOf(member))
167841
+ directed.set(effectiveNameOf(member), member);
167807
167842
  }
167808
167843
  const pinOf = (member, id2, parentPort) => ({
167809
167844
  id: id2,
167810
- name: nameOf2(member),
167811
- type: typeText(member),
167845
+ name: effectiveNameOf(member),
167846
+ type: typeText(member) ?? inheritedTypes.get(lastSeg(redefinitionTargetsOf(member)[0] ?? effectiveNameOf(member))),
167812
167847
  direction: directionOf(member),
167813
167848
  conjugated: isConjugated(member) || void 0,
167814
167849
  // REQ-194/REQ-408 - see the port rule above: only a written `ref` dashes.
@@ -167821,7 +167856,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167821
167856
  // part does, so Properties inspects it the same way in the AFV as it
167822
167857
  // inspects a port in the IV.
167823
167858
  compartments: this.compartmentsFor(member, uri, index2, true),
167824
- source: sourceOf2(member, uri)
167859
+ source: sourceOf2(member, uri),
167860
+ meta: valueTextOf(member) !== void 0 ? { boundArgument: valueTextOf(member) } : void 0
167825
167861
  });
167826
167862
  const pins = [];
167827
167863
  const addNestedPins = (param, paramId, depth, seenTypes) => {
@@ -167847,12 +167883,38 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167847
167883
  }
167848
167884
  };
167849
167885
  for (const member of directed.values()) {
167850
- const id2 = `${ownerId}.${nameOf2(member)}`;
167886
+ const id2 = `${ownerId}.${effectiveNameOf(member)}`;
167851
167887
  pins.push(pinOf(member, id2));
167852
167888
  addNestedPins(member, id2, 1, /* @__PURE__ */ new Set());
167853
167889
  }
167890
+ if (isCalcDecl(action)) {
167891
+ const result = effectiveCalculationResult(action, (name) => index2.get(name));
167892
+ if (result) {
167893
+ const parameter = result.parameter;
167894
+ const name = parameter ? nameOf2(parameter) ?? "result" : "result";
167895
+ pins.push({
167896
+ id: `${ownerId}.${name}`,
167897
+ name,
167898
+ direction: "out",
167899
+ pin: true,
167900
+ isDef: false,
167901
+ type: parameter ? typeText(parameter) : void 0,
167902
+ source: sourceOf2(parameter ?? action, uri),
167903
+ meta: { calculationResult: true, inherited: result.inherited }
167904
+ });
167905
+ }
167906
+ }
167854
167907
  return pins;
167855
167908
  }
167909
+ // REQ-208 - Only a locally declared result expression is editable on a usage.
167910
+ calculationValue(node, index2) {
167911
+ if (!isCalcDecl(node))
167912
+ return {};
167913
+ const result = effectiveCalculationResult(node, (name) => index2.get(name));
167914
+ if (node.isDef !== true && (!result?.expression || result.inherited))
167915
+ return {};
167916
+ return { value: result?.expression?.$cstNode?.text.replace(/\s+/g, " ").trim() ?? "" };
167917
+ }
167856
167918
  /** REQ-196/198: one definition node per type used by a drawn usage.
167857
167919
  * The Defs filter controls these nodes. An existing definition tile is
167858
167920
  * reused. Typing links are omitted by user direction (2026-09-06).
@@ -167969,8 +168031,10 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167969
168031
  }
167970
168032
  return (isPackage(node) || isDocument(node)) && this.nestedUsages(node, isPartDecl).some((n2) => nameOf2(n2) !== void 0);
167971
168033
  case "afv": {
168034
+ if (isCalcDecl(node))
168035
+ return true;
167972
168036
  if (isActionDecl(node) || this.isCaseLike(node)) {
167973
- const actions = membersOf(node).map((member) => this.thenActionMemberNode(member) ?? member).filter(isActionDecl);
168037
+ const actions = membersOf(node).map((member) => this.thenActionMemberNode(member) ?? member).filter((n2) => isActionDecl(n2) || isCalcDecl(n2));
167974
168038
  if (actions.some((n2) => nameOf2(n2) !== void 0))
167975
168039
  return true;
167976
168040
  return node.inlineBody !== void 0;
@@ -168202,8 +168266,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168202
168266
  const attributeValue = (node) => valueTextOf(node) ?? node.default?.value?.$cstNode?.text ?? "";
168203
168267
  const returnMemberOf = (node) => membersOf(node).find((m) => m.$type === "ReturnDecl");
168204
168268
  const expressionBody = (node) => {
168205
- const ret = isCalcDecl(node) ? returnMemberOf(node) : void 0;
168206
- const text = ret ? valueTextOf(ret) ?? "" : node.body?.$cstNode?.text ?? "";
168269
+ const result = isCalcDecl(node) ? effectiveCalculationResult(node, (name) => index2.get(name)) : void 0;
168270
+ const text = isCalcDecl(node) ? result && !result.inherited ? result.expression?.$cstNode?.text ?? "" : "" : node.body?.$cstNode?.text ?? "";
168207
168271
  return text.replace(/\s+/g, " ").trim();
168208
168272
  };
168209
168273
  const declaresExpression = (node) => node.body !== void 0 || isCalcDecl(node) && valueTextOf(returnMemberOf(node)) !== void 0;
@@ -168649,27 +168713,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168649
168713
  }
168650
168714
  }
168651
168715
  }
168652
- const CLAIM_EDGE_KINDS = {
168653
- AssertConstraintStmt: "assert",
168654
- AssumeConstraintStmt: "assume",
168655
- RequireConstraintStmt: "require"
168656
- };
168657
168716
  for (const owner of [...defNodes, ...usageNodes]) {
168658
- for (const m of membersOf(owner)) {
168659
- const claim = CLAIM_EDGE_KINDS[m.$type];
168660
- if (!claim)
168661
- continue;
168662
- const negated = m.isNegated === true;
168663
- const label = `\xAB${claim}${negated ? " not" : ""}\xBB`;
168664
- const reference = constraintReferenceOf(m);
168665
- if (reference) {
168666
- linkByName(claim, owner, reference.path, label, m);
168667
- continue;
168668
- }
168669
- const typed = typeText(m);
168670
- if (typed)
168671
- linkByName(claim, owner, typed, label, m);
168672
- }
168717
+ this.emitConstraintGraphics(owner, semanticId(owner), membersOf(owner), nodes, edges, index2, uri, false);
168673
168718
  }
168674
168719
  const allocPairs = [
168675
168720
  ...all.filter((m) => m.$type === "AllocateStmt").map((a2) => ({
@@ -169306,6 +169351,89 @@ ${node.id}`;
169306
169351
  out.push({ title: "flows", items: rows });
169307
169352
  return out;
169308
169353
  }
169354
+ // REQ-209 - Claims preserve usage identity; references reuse their target.
169355
+ emitConstraintGraphics(owner, ownerId, members, nodes, edges, index2, uri, interconnection) {
169356
+ const kinds = {
169357
+ AssertConstraintStmt: "assert",
169358
+ AssumeConstraintStmt: "assume",
169359
+ RequireConstraintStmt: "require"
169360
+ };
169361
+ const ensureNode = (target, id2, keyword) => {
169362
+ let node = nodes.find((candidate) => candidate.id === id2);
169363
+ if (!node) {
169364
+ const body = target.body?.$cstNode?.text;
169365
+ node = {
169366
+ id: id2,
169367
+ name: effectiveNameOf(target) ?? keyword,
169368
+ keyword,
169369
+ shape: "box",
169370
+ isDef: target.isDef === true,
169371
+ type: typeText(target),
169372
+ multiplicity: this.multText(target),
169373
+ compartments: this.compartmentsFor(target, uri, index2),
169374
+ ...(isConstraintDecl(target) || isInlineConstraintClaim(target)) && (body !== void 0 || target.isDef) ? { value: (body ?? "").replace(/\s+/g, " ").trim() } : {},
169375
+ ...interconnection ? { ports: this.actionPinPorts(target, id2, index2, uri) } : {},
169376
+ source: sourceOf2(target, uri),
169377
+ ...interconnection ? {} : { parent: ownerId },
169378
+ meta: { constraintUsage: true, ...!interconnection ? { gvUsage: true } : {} }
169379
+ };
169380
+ nodes.push(node);
169381
+ }
169382
+ node.meta = { ...node.meta, constraintUsage: true };
169383
+ return node;
169384
+ };
169385
+ for (const member of members) {
169386
+ const kind = kinds[member.$type];
169387
+ if (!kind) {
169388
+ if (interconnection && isConstraintDecl(member) && !member.isDef) {
169389
+ ensureNode(member, `${ownerId}::${effectiveNameOf(member) ?? member.$cstNode?.offset}`, "constraint");
169390
+ }
169391
+ continue;
169392
+ }
169393
+ const reference = constraintReferenceOf(member);
169394
+ let target = member;
169395
+ let targetId = `${ownerId}::${effectiveNameOf(member) ?? `#${member.$type}:${members.filter((m) => m.$type === member.$type).indexOf(member) + 1}`}`;
169396
+ if (reference) {
169397
+ const resolved = this.featurePaths.resolveDeclarationPath(member);
169398
+ const localPath = reference.path === reference.simpleName || reference.path.replaceAll("::", ".") === `self.${reference.simpleName}`;
169399
+ const effective = interconnection && localPath ? members.find((candidate) => (isConstraintDecl(candidate) || isInlineConstraintClaim(candidate)) && [effectiveNameOf(candidate), ...redefinitionTargetsOf(candidate).map(lastSeg)].includes(reference.simpleName)) : void 0;
169400
+ const found = effective ?? resolved.segments.at(-1)?.target ?? membersOf(owner).find((candidate) => candidate !== member && nameOf2(candidate) === reference.path) ?? index2.get(reference.path) ?? index2.get(reference.simpleName);
169401
+ if (!found || found === member)
169402
+ continue;
169403
+ target = found;
169404
+ targetId = interconnection && members.includes(target) && effectiveNameOf(target) ? `${ownerId}::${effectiveNameOf(target)}` : qnameOf(target) || reference.path;
169405
+ }
169406
+ const negated = member.isNegated === true;
169407
+ ensureNode(target, targetId, reference ? keywordFor(target) : `${kind}${negated ? " not" : ""} constraint`);
169408
+ const id2 = `${ownerId}::__${kind}_${member.$cstNode?.offset ?? edges.length}`;
169409
+ if (!edges.some((edge) => edge.id === id2))
169410
+ edges.push({
169411
+ id: id2,
169412
+ from: ownerId,
169413
+ to: targetId,
169414
+ kind,
169415
+ label: `\xAB${kind}${negated ? " not" : ""}\xBB`,
169416
+ source: sourceOf2(member, uri),
169417
+ meta: { constraintReference: !!reference, negated }
169418
+ });
169419
+ if (!reference && !interconnection) {
169420
+ const definition = this.resolveType(member, index2);
169421
+ const definitionId = definition ? qnameOf(definition) : void 0;
169422
+ if (definitionId && nodes.some((node) => node.id === definitionId) && !edges.some((edge) => edge.kind === "definedBy" && edge.from === targetId)) {
169423
+ edges.push({
169424
+ id: `${id2}_type`,
169425
+ from: targetId,
169426
+ to: definitionId,
169427
+ kind: "definedBy",
169428
+ source: sourceOf2(member, uri)
169429
+ });
169430
+ }
169431
+ }
169432
+ }
169433
+ const drawnOwner = nodes.find((node) => node.id === ownerId);
169434
+ if (drawnOwner && interconnection)
169435
+ drawnOwner.compartments = withoutCompartments(drawnOwner.compartments, "assert constraints", "assume constraints", "require constraints");
169436
+ }
169309
169437
  // REQ-192/360/363 — the ONE recursive Interconnection View renderer. Given the
169310
169438
  // top-level `roots` to draw, it renders each part instance and nests its
169311
169439
  // internals recursively, drilling into a usage's type DEFINITION (+
@@ -169358,8 +169486,13 @@ ${node.id}`;
169358
169486
  const emptyPrefixFlow = isEmptyPrefixFlow(member);
169359
169487
  const emptyPrefixInterface = isInterfaceDecl(member) && relationship.target !== void 0 && relationship.connect === void 0;
169360
169488
  const namedRelationship = isConnectorDecl(member) || isConnectionDecl(member) || isInterfaceDecl(member) && !emptyPrefixInterface || member.$type === "BindingDecl" || member.$type === "FlowStmt" && !emptyPrefixFlow;
169361
- if (namedRelationship) {
169362
- const names = [nameOf2(member), redefinedNameOf(member)].filter((name) => name !== void 0);
169489
+ const constraint = isConstraintDecl(member) || isInlineConstraintClaim(member);
169490
+ if (namedRelationship || constraint) {
169491
+ const names = [
169492
+ nameOf2(member),
169493
+ redefinedNameOf(member),
169494
+ ...constraint ? redefinitionTargetsOf(member).map(lastSeg) : []
169495
+ ].filter((name) => name !== void 0);
169363
169496
  if (names.some((name) => claimedNames.has(name)))
169364
169497
  continue;
169365
169498
  for (const name of names)
@@ -169608,6 +169741,10 @@ ${node.id}`;
169608
169741
  const resolver = childResolver([], { usage: part, id: instanceId });
169609
169742
  this.emitInterconnectEdges(effectiveMembersOf(part), resolver.resolve, [], index2, uri, target.nodes, target.edges, eRef, instanceId, resolver.actionPinIds, resolver.structuralPortIds, part, occurrenceInherited, endpointProvenance);
169610
169743
  }
169744
+ this.emitConstraintGraphics(part, instanceId, effectiveMembersOf(part), target.nodes, target.edges, index2, uri, true);
169745
+ const claimFrame = target.frames.find((frame2) => frame2.id === instanceId);
169746
+ if (claimFrame)
169747
+ claimFrame.compartments = withoutCompartments(claimFrame.compartments, "assert constraints", "assume constraints", "require constraints");
169611
169748
  };
169612
169749
  const packageOfRoot = (root4) => enclosingPackage(root4) ?? scope;
169613
169750
  const rootBaseId = (root4) => qnameOf(root4) || nameOf2(root4) || `__root_${root4.$cstNode?.offset ?? 0}__`;
@@ -169699,8 +169836,35 @@ ${node.id}`;
169699
169836
  // single Interconnection View (called once over the anchor's whole subtree)
169700
169837
  // and the package overview (called once per container part, tagging any
169701
169838
  // synthetic n-ary dot with that container's frame id).
169702
- emitInterconnectEdges(members, resolveEnd, parts, index2, uri, nodes, edges, eRef, frameId, actionPinIds = /* @__PURE__ */ new Set(), structuralPortIds = /* @__PURE__ */ new Set(), memberOwner, memberOwnerInherited = false, endpointProvenance = /* @__PURE__ */ new Map()) {
169839
+ emitInterconnectEdges(members, resolveStructuralEnd, parts, index2, uri, nodes, edges, eRef, frameId, actionPinIds = /* @__PURE__ */ new Set(), structuralPortIds = /* @__PURE__ */ new Set(), memberOwner, memberOwnerInherited = false, endpointProvenance = /* @__PURE__ */ new Map()) {
169703
169840
  const memberList2 = [...members];
169841
+ if (memberOwner && frameId)
169842
+ this.emitConstraintGraphics(memberOwner, frameId, memberList2, nodes, edges, index2, uri, true);
169843
+ const resolveEnd = (pathText) => {
169844
+ const structural = resolveStructuralEnd(pathText);
169845
+ if (structural)
169846
+ return structural;
169847
+ const path10 = pathText.replaceAll("::", ".");
169848
+ const candidates = nodes.filter((node) => node.meta?.constraintUsage === true && (!frameId || node.id.startsWith(`${frameId}::`) || edges.some((edge) => edge.from === frameId && edge.to === node.id))).flatMap((node) => (node.ports ?? []).filter((pin) => `${node.name}.${pin.name}` === path10 || pin.id.replaceAll("::", ".") === path10));
169849
+ if (candidates.length === 1)
169850
+ return candidates[0].id;
169851
+ const attribute = memberList2.find((member) => isAttributeDecl(member) && nameOf2(member) === path10);
169852
+ if (!attribute || !frameId)
169853
+ return void 0;
169854
+ const id2 = `${frameId}::${nameOf2(attribute)}`;
169855
+ if (!nodes.some((node) => node.id === id2))
169856
+ nodes.push({
169857
+ id: id2,
169858
+ name: nameOf2(attribute),
169859
+ keyword: "attribute",
169860
+ isDef: false,
169861
+ shape: "box",
169862
+ type: typeText(attribute),
169863
+ value: valueTextOf(attribute),
169864
+ source: sourceOf2(attribute, uri)
169865
+ });
169866
+ return id2;
169867
+ };
169704
169868
  const pendingFlows = [];
169705
169869
  const firstEdge = edges.length;
169706
169870
  const relationshipFields = (source) => {
@@ -170247,7 +170411,7 @@ ${node.id}`;
170247
170411
  while (cur && cur !== scope) {
170248
170412
  if (set3.has(cur)) {
170249
170413
  if (kind !== "stv" || !isPartDecl(n2)) {
170250
- if (kind === "afv" && isPartDecl(cur) && n2.isDef === true && isActionDecl(n2))
170414
+ if (kind === "afv" && isPartDecl(cur) && n2.isDef === true && (isActionDecl(n2) || isCalcDecl(n2)))
170251
170415
  break;
170252
170416
  return false;
170253
170417
  }
@@ -170301,12 +170465,12 @@ ${node.id}`;
170301
170465
  anchor: sub,
170302
170466
  diagramRoot: ctx.diagramRoot ?? ctx.anchor
170303
170467
  });
170304
- if (this.diagramElementCount(subModel) === 0 && !(kind === "afv" && isActionDecl(sub)) && !(kind === "stv" && (isStateDecl(sub) || isExhibitStateDecl(sub))))
170468
+ if (this.diagramElementCount(subModel) === 0 && !(kind === "afv" && (isActionDecl(sub) || isCalcDecl(sub))) && !(kind === "stv" && (isStateDecl(sub) || isExhibitStateDecl(sub))))
170305
170469
  continue;
170306
170470
  const frameId = `t${tile}`;
170307
170471
  const q = qnameOf(sub) || nameOf2(sub) || "";
170308
170472
  const rel2 = scopeQ && q.startsWith(`${scopeQ}::`) ? q.slice(scopeQ.length + 2) : nameOf2(sub) ?? q;
170309
- const tileMetaTag = kind === "afv" ? isPartDecl(sub) ? { afvPart: true } : isActionDecl(sub) && sub.isDef === true ? { behaviorDef: true } : void 0 : kind === "stv" && sub.isDef === true ? { behaviorDef: true } : void 0;
170473
+ const tileMetaTag = kind === "afv" ? isPartDecl(sub) ? { afvPart: true } : (isActionDecl(sub) || isCalcDecl(sub)) && sub.isDef === true ? { behaviorDef: true } : void 0 : kind === "stv" && sub.isDef === true ? { behaviorDef: true } : void 0;
170310
170474
  const tileIdentity = {
170311
170475
  declarationId: qnameOf(sub) || nameOf2(sub),
170312
170476
  declarationName: nameOf2(sub),
@@ -170527,7 +170691,7 @@ ${node.id}`;
170527
170691
  addStepAlias(alias, id2);
170528
170692
  };
170529
170693
  let anon = 0;
170530
- const anchorHostsFlow = isActionDecl(anchor) || this.isCaseLike(anchor);
170694
+ const anchorHostsFlow = isActionDecl(anchor) || isCalcDecl(anchor) || this.isCaseLike(anchor);
170531
170695
  const exprTextOf = (v) => (v?.$cstNode?.text ?? (typeof v === "string" ? v : "")).replace(/\s+/g, " ").trim();
170532
170696
  const performPathOf = (m) => [nameOf2(m), ...m.chainSegs ?? []].filter((segment) => !!segment);
170533
170697
  const performLabel = (m) => performPathOf(m).at(-1) ?? typeText(m) ?? "perform";
@@ -170536,6 +170700,9 @@ ${node.id}`;
170536
170700
  const flowNodeInfo = (m) => {
170537
170701
  const mm = m;
170538
170702
  switch (m.$type) {
170703
+ // REQ-208 - Calculations share action occurrence docking.
170704
+ case "CalcDecl":
170705
+ return { shape: "action", label: nameOf2(m) ?? "calc" };
170539
170706
  case "ActionDecl": {
170540
170707
  if (!nameOf2(m))
170541
170708
  return void 0;
@@ -170770,7 +170937,7 @@ ${node.id}`;
170770
170937
  pinByPath.set(alias, null);
170771
170938
  };
170772
170939
  const actionPinsOf = (action, ownerId) => {
170773
- if (!isActionDecl(action) && action.$type !== "PerformStmt")
170940
+ if (!isActionDecl(action) && !isCalcDecl(action) && action.$type !== "PerformStmt")
170774
170941
  return [];
170775
170942
  const pins = this.actionPinPorts(action, ownerId, index2, uri);
170776
170943
  const inline = action.inlineBody;
@@ -170955,7 +171122,7 @@ ${node.id}`;
170955
171122
  frameOfNode.set(holder, frameId);
170956
171123
  const framePorts = isPartDecl(holder) ? [...framePins, ...this.portsOf(holder, index2, uri, frameId).ports.filter((port) => !framePins.some((pin) => pin.name === port.name)).map((port) => ({ ...port, meta: { ...port.meta, afvPartPort: true } }))] : framePins;
170957
171124
  registerPins(holder, frameId, framePins, isAnchor);
170958
- const frameCompartments = isActionDecl(holder) ? actionFeatureCompartments(holder) : withoutCompartments(this.compartmentsFor(holder, uri, index2), ...graphicalAfvTitles);
171125
+ const frameCompartments = isActionDecl(holder) || isCalcDecl(holder) ? actionFeatureCompartments(holder) : withoutCompartments(this.compartmentsFor(holder, uri, index2), ...graphicalAfvTitles);
170959
171126
  frames.push({
170960
171127
  id: frameId,
170961
171128
  label: nameOf2(holder) ?? lastSeg(frameId),
@@ -170964,6 +171131,7 @@ ${node.id}`;
170964
171131
  isDef: holder.isDef === true,
170965
171132
  type: typeText(holder),
170966
171133
  multiplicity: this.multText(holder),
171134
+ ...this.calculationValue(holder, index2),
170967
171135
  compartments: frameCompartments?.length ? frameCompartments : void 0,
170968
171136
  ports: framePorts,
170969
171137
  source: sourceOf2(holder, uri),
@@ -171000,7 +171168,7 @@ ${node.id}`;
171000
171168
  this.featurePath(namedSendNode.via) ? `via ${this.featurePath(namedSendNode.via)}` : void 0,
171001
171169
  this.featurePath(namedSendNode.sendTarget) ? `to ${this.featurePath(namedSendNode.sendTarget)}` : void 0
171002
171170
  ].filter((value) => !!value).join(" ") : void 0;
171003
- const featureCompartments = m.$type === "ActionDecl" || m.$type === "PerformStmt" ? actionFeatureCompartments(m) : [];
171171
+ const featureCompartments = isCalcDecl(m) || m.$type === "ActionDecl" || m.$type === "PerformStmt" ? actionFeatureCompartments(m) : [];
171004
171172
  const detailCompartment = acceptTrigger || sendDetail ? [{
171005
171173
  title: acceptTrigger ? "accepts" : "sends",
171006
171174
  items: [{ text: acceptTrigger ?? sendDetail, source: sourceOf2(inlineAccept ?? m, uri) }]
@@ -171013,6 +171181,8 @@ ${node.id}`;
171013
171181
  keyword: info.keyword ?? keywordFor(m),
171014
171182
  isDef: m.isDef === true,
171015
171183
  shape: info.shape,
171184
+ ...isCalcDecl(m) ? { type: typeText(m), multiplicity: this.multText(m) } : {},
171185
+ ...this.calculationValue(m, index2),
171016
171186
  ports: pins,
171017
171187
  compartments: compartments.length ? compartments : void 0,
171018
171188
  frame: frameId,
@@ -171114,14 +171284,14 @@ ${node.id}`;
171114
171284
  }) || inline?.$type === "AssignNode" || definitionFlowMembersOf(node).length > 0;
171115
171285
  };
171116
171286
  const contentSourceOf = (node) => {
171117
- if (node.$type === "ActionDecl")
171287
+ if (isActionDecl(node) || isCalcDecl(node))
171118
171288
  return ownsFlow(node) ? node : void 0;
171119
171289
  if (node.$type !== "PerformStmt")
171120
171290
  return void 0;
171121
171291
  if (ownsFlow(node))
171122
171292
  return node;
171123
171293
  const target = performTarget(node);
171124
- return target && isActionDecl(target) && ownsFlow(target) ? target : void 0;
171294
+ return target && (isActionDecl(target) || isCalcDecl(target)) && ownsFlow(target) ? target : void 0;
171125
171295
  };
171126
171296
  const isCompositeAction = (node) => contentSourceOf(node) !== void 0;
171127
171297
  const openFrames = /* @__PURE__ */ new Set();
@@ -171156,6 +171326,7 @@ ${node.id}`;
171156
171326
  isDef: action.isDef === true,
171157
171327
  type: typeText(action),
171158
171328
  multiplicity: this.multText(action),
171329
+ ...this.calculationValue(action, index2),
171159
171330
  compartments: actionCompartments.length ? actionCompartments : void 0,
171160
171331
  ports: pins,
171161
171332
  source: sourceOf2(action, uri),
@@ -174745,7 +174916,7 @@ ${node.id}`;
174745
174916
  add("objective", members.filter(isObjectiveDecl).filter(isNonVariantMember).map((m) => item(m, nameOf2(m) ?? "objective")));
174746
174917
  add("verifies", members.filter((m) => m.$type === "VerifyStmt").map((m) => item(m, String(m.target ?? ""))));
174747
174918
  add("verification methods", members.filter((m) => isMetadataDecl(m) && (typeText(m) ?? "").includes("VerificationMethod")).map((m) => item(m, typeText(m) ?? "method")));
174748
- const behavioralParameterOwner = isActionDecl(node) || isCalcDecl(node) || isConstraintDecl(node) || isCaseDecl(node) || isAnalysisCaseDecl(node) || isVerificationCaseDecl(node) || isUseCaseDecl(node);
174919
+ const behavioralParameterOwner = ["AssertConstraintStmt", "AssumeConstraintStmt", "RequireConstraintStmt"].includes(node.$type) || isActionDecl(node) || isCalcDecl(node) || isConstraintDecl(node) || isCaseDecl(node) || isAnalysisCaseDecl(node) || isVerificationCaseDecl(node) || isUseCaseDecl(node);
174749
174920
  const enclosingBehavioralParameterOwner = (() => {
174750
174921
  if (isPartDecl(node) || isPortDecl(node) || directionOf(node) === void 0)
174751
174922
  return false;
@@ -174764,9 +174935,17 @@ ${node.id}`;
174764
174935
  ...members.filter((m) => directionOf(m) !== void 0 && nameOf2(m)).filter((m) => !isVariantMember(m) && !isVariantConfigMember(m)).map((m) => item(m, canonicalFeatureText(m))),
174765
174936
  ...inheritedRows((member) => directionOf(member) !== void 0 && isNonVariantMember(member))
174766
174937
  ]);
174767
- add("result", members.filter((m) => m.$type === "ReturnDecl").map((m) => item(m, `return${nameOf2(m) ? ` ${nameOf2(m)}` : ""}${typeText(m) ? ` : ${typeText(m)}` : ""}`)));
174768
- if (isConstraintDecl(node))
174938
+ const calculationResult = isCalcDecl(node) ? effectiveCalculationResult(node, (name) => index2?.get(name)) : void 0;
174939
+ const results = calculationResult?.parameter ? [calculationResult.parameter] : members.filter((m) => m.$type === "ReturnDecl");
174940
+ add("result", results.map((m) => item(m, `${calculationResult?.inherited ? "^" : ""}return${nameOf2(m) ? ` ${nameOf2(m)}` : ""}${typeText(m) ? ` : ${typeText(m)}` : ""}${valueTextOf(m) ? ` = ${valueTextOf(m)}` : ""}`)));
174941
+ if (calculationResult?.expression)
174942
+ add("expression", [{
174943
+ text: calculationResult.expression.$cstNode?.text ?? "",
174944
+ source: sourceOf2(calculationResult.expression, uri)
174945
+ }]);
174946
+ if (isConstraintDecl(node) || ["AssertConstraintStmt", "AssumeConstraintStmt", "RequireConstraintStmt"].includes(node.$type)) {
174769
174947
  add("expression", exprText4(node) ? [{ text: exprText4(node), source: sourceOf2(node, uri) }] : []);
174948
+ }
174770
174949
  const enumValueMembers = isEnumDecl(node) ? members.filter(isOwnedEnumerationValue) : members.filter((m) => isNonVariantMember(m) && m.$type === "EnumValueDecl" && !m.typing);
174771
174950
  add("enums", enumValueMembers.map((m) => {
174772
174951
  const nm = nameOf2(m);
@@ -175124,7 +175303,7 @@ function projectGvTree(input) {
175124
175303
  visible.add(node.id);
175125
175304
  const isPackage2 = node.shape === "package";
175126
175305
  const children2 = (index2.children.get(node.id) ?? []).filter(
175127
- (child) => child.meta?.gvTreeExcluded !== true && !isDerivedHub(child) && (isPackage2 || isGvPartUsage(child))
175306
+ (child) => child.meta?.gvTreeExcluded !== true && !isDerivedHub(child) && (isPackage2 || isGvPartUsage(child) || child.meta?.constraintUsage === true)
175128
175307
  );
175129
175308
  const key2 = gvElementKey(node);
175130
175309
  const canExpand = children2.length > 0;
@@ -175134,7 +175313,7 @@ function projectGvTree(input) {
175134
175313
  ...node,
175135
175314
  // Tree states membership with a line. It does not borrow canvas containment.
175136
175315
  parent: void 0,
175137
- compartments: (isPackage2 && open ? void 0 : gvPackageMemberCompartments(node, index2))?.filter((compartment) => !open || !isPackage2 && compartment.title !== "parts").map((compartment) => ({ ...compartment, expandable: false, expanded: false })),
175316
+ compartments: (isPackage2 && open ? void 0 : gvPackageMemberCompartments(node, index2))?.filter((compartment) => !open || !isPackage2 && !["parts", "assert constraints", "assume constraints", "require constraints"].includes(compartment.title)).map((compartment) => ({ ...compartment, expandable: false, expanded: false })),
175138
175317
  meta: {
175139
175318
  ...node.meta,
175140
175319
  gvMode: "tree",
@@ -191059,7 +191238,7 @@ function nodeBlockForCanvas(node) {
191059
191238
  compartments: compartments2?.length ? compartments2 : void 0
191060
191239
  };
191061
191240
  }
191062
- if (!/\bconstraint(?:\s+def)?$/u.test(node.keyword.trim().toLowerCase())) return node;
191241
+ if (!/\b(?:constraint|calc)(?:\s+def)?$/u.test(node.keyword.trim().toLowerCase())) return node;
191063
191242
  const compartments = node.compartments?.filter((compartment) => compartment.title !== "expression");
191064
191243
  return {
191065
191244
  ...node,
@@ -192222,6 +192401,8 @@ function modelToFlow(model, opts) {
192222
192401
  nodes.push({
192223
192402
  id: f.id,
192224
192403
  type: "frame",
192404
+ // REQ-361 - Portless GV bounds include the complete painted frame.
192405
+ className: model.kind === "gv" && !f.ports?.length ? "rf-bounded-paint" : void 0,
192225
192406
  position: { x: 0, y: 0 },
192226
192407
  data: {
192227
192408
  kind: model.kind,
@@ -192337,6 +192518,8 @@ function modelToFlow(model, opts) {
192337
192518
  nodes.push({
192338
192519
  id: n2.id,
192339
192520
  type: isGeo ? "geo" : "sysml",
192521
+ // REQ-361 - Outside port labels must never enter paint containment.
192522
+ className: model.kind === "gv" && !n2.ports?.length ? "rf-bounded-paint" : void 0,
192340
192523
  position: { x: 0, y: 0 },
192341
192524
  data: {
192342
192525
  kind: model.kind,
@@ -208164,6 +208347,15 @@ svg.react-flow__connectionline { z-index: 1; }
208164
208347
  .rf-ctl-zoom { width: auto; min-width: 42px; padding: 0 4px; text-align: center; font: 600 11px var(--font-mono); color: var(--fg-2); }
208165
208348
  .rf-ctl-sep { width: 1px; height: 16px; background: var(--line); margin: 0 3px; }
208166
208349
  .react-flow__node { font-family: var(--font-mono); font-size: 11px; color: var(--fg-0); }
208350
+ /* REQ-361 - Bound hit testing on portless GV cards. The margin retains border
208351
+ * strokes, package tabs and side handles. Port glyphs and their outside labels
208352
+ * remain unbounded. Editing and hover controls also retain their full overflow. */
208353
+ /* Keep every card mounted. Only ordinary Group cards defer off-screen contents.
208354
+ * Their explicit dimensions preserve geometry. Large frames and Tree retain
208355
+ * their regular rendering path; deferring their contents adds layout work. */
208356
+ .react-flow__node.rf-bounded-paint { contain: paint; overflow-clip-margin: 16px; }
208357
+ .rf-gv-group .react-flow__node-sysml.rf-bounded-paint { content-visibility: auto; }
208358
+ .react-flow__node.rf-bounded-paint:is(:hover, :focus-within, .selected) { contain: none; content-visibility: visible; }
208167
208359
  .react-flow__node-sysml, .react-flow__node-frame { background: transparent; border: none; padding: 0; border-radius: 0; box-shadow: none; }
208168
208360
  .rf-node, .rf-frame { position: relative; user-select: none; }
208169
208361
  /* Container frames: the interior is click-through so connectors drawn underneath
@@ -208999,6 +209191,8 @@ var TOOLBOX = {
208999
209191
  // they are the action's parameter pins.
209000
209192
  afv: {
209001
209193
  elements: [
209194
+ el("calc def", "calc def", "\u2211"),
209195
+ el("calc", "calc", "\u2211"),
209002
209196
  el("action def", "action def", "\u25B7"),
209003
209197
  el("action", "action", "\u25B7"),
209004
209198
  // issue #217 — a behaviour-root canvas writes into the action
@@ -209212,6 +209406,7 @@ var CHILD_KINDS = {
209212
209406
  function normalizedElementKeyword(keyword) {
209213
209407
  const normalized = keyword.trim().toLowerCase();
209214
209408
  let base = normalized.replace(/\s*def$/u, "").replace(/^(?:(?:abstract|variation|variant|ref|derived|readonly|individual|timeslice|snapshot|parallel)\s+)+/u, "").trim();
209409
+ if (/^(assert(?: not)?|assume|require) constraint$/u.test(base)) base = "constraint";
209215
209410
  const isDecision = base === "decide" || base === "decision";
209216
209411
  if ([
209217
209412
  "send action",
@@ -209343,6 +209538,7 @@ function relationToolsForNode(keyword, viewKind, shape) {
209343
209538
  return tool.kind === "interface" ? base === "port" : true;
209344
209539
  }
209345
209540
  if (viewKind === "afv") {
209541
+ if (base === "calc") return tool.kind === "succession" || tool.kind === "begin";
209346
209542
  if (base === "action") return tool.kind === "succession" || tool.kind === "begin" || tool.kind === "guard" || tool.kind === "terminate" || tool.kind === TERMINAL_RELATION;
209347
209543
  if (base === "control") return tool.kind === "succession" || tool.kind === "begin" || tool.kind === "guard" || isDecision && tool.kind === "else" || tool.kind === "terminate" || tool.kind === TERMINAL_RELATION;
209348
209544
  if (base === "port") return false;
@@ -210197,7 +210393,7 @@ function titleNameLine(name, type, mult, alias, availPx) {
210197
210393
  return { full, shown, clipped: shown !== full };
210198
210394
  }
210199
210395
  function valueFieldRole(keyword) {
210200
- if (keyword.startsWith("constraint")) return { label: "Constraint", placeholder: "Add constraint" };
210396
+ if (/\bconstraint(?:\s+def)?$/u.test(keyword)) return { label: "Constraint", placeholder: "Add constraint" };
210201
210397
  if (keyword.startsWith("calc")) return { label: "Expression", placeholder: "Add expression" };
210202
210398
  return { label: "Value", placeholder: "Add value" };
210203
210399
  }
@@ -212453,7 +212649,7 @@ async function runExport(command) {
212453
212649
  }
212454
212650
 
212455
212651
  // src/main.ts
212456
- var VERSION2 = true ? "0.38.0" : "dev";
212652
+ var VERSION2 = true ? "0.39.0" : "dev";
212457
212653
  function display(file) {
212458
212654
  const rel2 = path9.relative(process.cwd(), file);
212459
212655
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;