sysml-diagram 0.25.0 → 0.27.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
@@ -162649,7 +162649,12 @@ function keywordFor(node) {
162649
162649
  JoinNode: "join",
162650
162650
  TerminateNode: "terminate",
162651
162651
  PerformStmt: "perform action",
162652
- EventDecl: "event occurrence"
162652
+ EventDecl: "event occurrence",
162653
+ // REQ-198, issue #113: a state sub-action drawn as a state-transition
162654
+ // node keeps the keyword that says WHEN it runs (OMG 8.2.2.18.1).
162655
+ EntryAction: "entry action",
162656
+ DoAction: "do action",
162657
+ ExitAction: "exit action"
162653
162658
  };
162654
162659
  if (t === "ActionDecl" && n2.inlineBody?.$type === "AcceptNode") {
162655
162660
  return "accept action";
@@ -162698,6 +162703,11 @@ var ACTION_FLOW_STEP_TYPES = /* @__PURE__ */ new Set([
162698
162703
  "ForLoopNode"
162699
162704
  ]);
162700
162705
  var isActionFlowStep = (node) => !!node && ACTION_FLOW_STEP_TYPES.has(node.$type);
162706
+ var NESTED_ACTION_LIMIT = 8;
162707
+ var withoutCompartments = (compartments, ...titles) => {
162708
+ const kept = (compartments ?? []).filter((compartment) => !titles.includes(compartment.title));
162709
+ return kept.length ? kept : void 0;
162710
+ };
162701
162711
  var modifiersOf = (node) => {
162702
162712
  const mods = node.modifiers;
162703
162713
  return Array.isArray(mods) ? mods : [];
@@ -163818,6 +163828,37 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163818
163828
  /** REQ-196 — is this `perform` a SWIMLANE claim (OMG p.90): a part claiming an
163819
163829
  * action that another action owns? Such a claim is drawn inside that action,
163820
163830
  * so it is never content of the part that wrote it. */
163831
+ /** issue #114 — the path a referencing `perform`/`exhibit` names, as written
163832
+ * (`Mission::prepare`, `flow1.step`). A referencing claim has no local name,
163833
+ * so the written path is the only thing that identifies it in a text row. */
163834
+ behaviorReferenceText(stmt) {
163835
+ return this.behaviorReferenceSegments(stmt).map((segment, at) => at === 0 ? segment.text : `${segment.separator ?? "::"}${segment.text}`).join("");
163836
+ }
163837
+ /** issue #114 — the `perform` claims of the document, grouped by the action
163838
+ * each one names, so an action can list the parts that perform it
163839
+ * (`performed by`, OMG SysML v2 §8.4.13.11 `Parts::Part::performedActions`).
163840
+ * One walk per parse: the reverse listing is otherwise quadratic in the
163841
+ * number of drawn nodes. */
163842
+ performClaimsByTarget(root4, index2) {
163843
+ const cache = this.perParse(root4);
163844
+ if (cache.performClaims)
163845
+ return cache.performClaims;
163846
+ const claims = /* @__PURE__ */ new Map();
163847
+ for (const node of this.rootContents(root4)) {
163848
+ if (node.$type !== "PerformStmt")
163849
+ continue;
163850
+ const target = this.performTargetOf(node, index2);
163851
+ if (!target || target === node)
163852
+ continue;
163853
+ const existing = claims.get(target);
163854
+ if (existing)
163855
+ existing.push(node);
163856
+ else
163857
+ claims.set(target, [node]);
163858
+ }
163859
+ cache.performClaims = claims;
163860
+ return claims;
163861
+ }
163821
163862
  isSwimlaneClaim(member, index2) {
163822
163863
  if (member.$type !== "PerformStmt")
163823
163864
  return false;
@@ -165326,7 +165367,10 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165326
165367
  isDef: part.isDef === true,
165327
165368
  type: typeText(part),
165328
165369
  multiplicity: multText(part),
165329
- compartments: this.compartmentsFor(part, uri, index2),
165370
+ // issue #114 — this frame already DRAWS the actions the part
165371
+ // performs, so their textual `perform actions` rows would be a
165372
+ // second depiction of the same members (REQ-181/185).
165373
+ compartments: withoutCompartments(this.compartmentsFor(part, uri, index2), "perform actions"),
165330
165374
  ports: ports.length ? ports : void 0,
165331
165375
  source: sourceOf2(part, uri),
165332
165376
  meta: editMeta
@@ -166688,7 +166732,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
166688
166732
  else
166689
166733
  emitFlowNode(child, ownerFrameId, holder, occurrence, laneParts);
166690
166734
  };
166691
- const actionFeatureCompartments = (action) => (this.compartmentsFor(action, uri, index2) ?? []).filter((compartment) => compartment.title !== "parameters" && compartment.title !== "actions");
166735
+ const graphicalAfvTitles = ["perform actions", "performed by"];
166736
+ const actionFeatureCompartments = (action) => withoutCompartments(this.compartmentsFor(action, uri, index2), "parameters", "actions", ...graphicalAfvTitles) ?? [];
166692
166737
  const renderContainer = (holder, frameId, parentFrame, depth, seen, isAnchor, inheritedExecutionContext, holderOccurrence = frameId) => {
166693
166738
  if (!occurrenceEdits.has(holderOccurrence)) {
166694
166739
  occurrenceEdits.set(holderOccurrence, {
@@ -166713,7 +166758,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
166713
166758
  frameOfNode.set(holder, frameId);
166714
166759
  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;
166715
166760
  registerPins(holder, frameId, framePins, isAnchor);
166716
- const frameCompartments = isActionDecl(holder) ? actionFeatureCompartments(holder) : this.compartmentsFor(holder, uri, index2);
166761
+ const frameCompartments = isActionDecl(holder) ? actionFeatureCompartments(holder) : withoutCompartments(this.compartmentsFor(holder, uri, index2), ...graphicalAfvTitles);
166717
166762
  frames.push({
166718
166763
  id: frameId,
166719
166764
  label: nameOf2(holder) ?? lastSeg(frameId),
@@ -166795,6 +166840,10 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
166795
166840
  return id2;
166796
166841
  };
166797
166842
  const emitLoopFrame = (loop, parentFrameId, executionContext, occurrence, laneParts, wrapper) => {
166843
+ if (nestingExhausted()) {
166844
+ emitFlowNode(wrapper ?? loop, parentFrameId, executionContext, occurrence, laneParts);
166845
+ return;
166846
+ }
166798
166847
  const banner = loopBanner(loop);
166799
166848
  const wrapperName = wrapper ? nameOf2(wrapper) : void 0;
166800
166849
  const loopId = occurrence;
@@ -166847,9 +166896,11 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
166847
166896
  const parentEdit = occurrenceEdits.get(occurrence);
166848
166897
  if (parentEdit)
166849
166898
  occurrenceEdits.set(bodyOccurrence, parentEdit);
166899
+ openFrames.add(loop);
166850
166900
  for (const bm of flowMembersOf(body)) {
166851
166901
  emitChild(bm, loopId, executionContext, bodyOccurrence);
166852
166902
  }
166903
+ openFrames.delete(loop);
166853
166904
  }
166854
166905
  idByNode.set(loop, loopId);
166855
166906
  registerStep(occurrence, loopId, [nameOf2(loop)]);
@@ -166876,10 +166927,11 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
166876
166927
  return target && isActionDecl(target) && ownsFlow(target) ? target : void 0;
166877
166928
  };
166878
166929
  const isCompositeAction = (node) => contentSourceOf(node) !== void 0;
166879
- const openActionFrames = /* @__PURE__ */ new Set();
166930
+ const openFrames = /* @__PURE__ */ new Set();
166931
+ const nestingExhausted = () => openFrames.size >= NESTED_ACTION_LIMIT;
166880
166932
  const emitCompositeActionFrame = (action, parentFrameId, executionContext, occurrence, laneParts) => {
166881
166933
  const content = contentSourceOf(action) ?? action;
166882
- if (openActionFrames.has(content)) {
166934
+ if (openFrames.has(content) || nestingExhausted()) {
166883
166935
  emitFlowNode(action, parentFrameId, executionContext, occurrence, laneParts);
166884
166936
  return;
166885
166937
  }
@@ -166925,12 +166977,16 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
166925
166977
  });
166926
166978
  recordLaneCandidate(id2, true, parentFrameId, laneParts, occurrence);
166927
166979
  actionRecords.push({ m: action, id: id2, performer: executionContext });
166928
- openActionFrames.add(content);
166980
+ openFrames.add(content);
166929
166981
  for (const child of flowMembersOf(content))
166930
166982
  emitChild(child, id2, executionContext, occurrence);
166931
- openActionFrames.delete(content);
166983
+ openFrames.delete(content);
166932
166984
  };
166933
166985
  const emitIfFrame = (node, parentFrameId, executionContext, occurrence, laneParts, wrapper) => {
166986
+ if (nestingExhausted()) {
166987
+ emitFlowNode(wrapper ?? node, parentFrameId, executionContext, occurrence, laneParts);
166988
+ return;
166989
+ }
166934
166990
  const inf = node;
166935
166991
  const declaredNode = wrapper ?? (nameOf2(node) ? node : void 0);
166936
166992
  const wrapperName = declaredNode ? nameOf2(declaredNode) : void 0;
@@ -166983,6 +167039,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
166983
167039
  idByNode.set(wrapper, frameId);
166984
167040
  registerStep(occurrence, frameId, [wrapperName]);
166985
167041
  }
167042
+ openFrames.add(node);
166986
167043
  for (const [branch, block2] of [
166987
167044
  ["then", inf.thenBody],
166988
167045
  ["else", inf.elseIf ?? inf.elseBody]
@@ -167014,6 +167071,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167014
167071
  [branch === "then" ? "structuredThenMemberIds" : "structuredElseMemberIds"]: memberIds
167015
167072
  };
167016
167073
  }
167074
+ openFrames.delete(node);
167017
167075
  };
167018
167076
  const anchorId = qnameOf(anchor) || nameOf2(anchor) || "__anchor__";
167019
167077
  renderContainer(anchor, anchorId, void 0, 0, /* @__PURE__ */ new Set(), true, void 0);
@@ -167142,6 +167200,15 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167142
167200
  branchInsertable: true
167143
167201
  } : void 0;
167144
167202
  };
167203
+ const drawnRowKeys = /* @__PURE__ */ new Set();
167204
+ const rowKey = (source) => source ? `${source.uri} ${source.range.start.line} ${source.range.start.character}` : void 0;
167205
+ const registerDrawn = (member) => {
167206
+ if (!member)
167207
+ return;
167208
+ const key = rowKey(sourceOf2(member, uri));
167209
+ if (key)
167210
+ drawnRowKeys.add(key);
167211
+ };
167145
167212
  const linkedSuccessions = /* @__PURE__ */ new Set();
167146
167213
  const linkIds = (from, to, label, src, meta) => {
167147
167214
  if (!from || !to)
@@ -167167,6 +167234,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167167
167234
  source: src ? sourceOf2(src, uri) : void 0,
167168
167235
  meta
167169
167236
  });
167237
+ registerDrawn(src);
167170
167238
  return true;
167171
167239
  };
167172
167240
  const linkInlineTerminate = (action, actionId) => {
@@ -167295,6 +167363,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167295
167363
  label: this.flowPayloadText(fm) ?? typeText(m) ?? transferredFeature ?? nameOf2(m),
167296
167364
  source: sourceOf2(m, uri)
167297
167365
  });
167366
+ registerDrawn(m);
167298
167367
  }
167299
167368
  } else if (m.$type === "BindStmt" || m.$type === "BindingConnectorStmt" || m.$type === "BindingDecl") {
167300
167369
  const binding = m;
@@ -167304,6 +167373,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167304
167373
  const to = resolvePin(right, occurrenceContext);
167305
167374
  if (from && to) {
167306
167375
  edges.push({ id: `e${e++}`, from, to, kind: "binding", label: "=", source: sourceOf2(m, uri) });
167376
+ registerDrawn(m);
167307
167377
  }
167308
167378
  } else {
167309
167379
  const memberName = localFlowName(m);
@@ -167593,6 +167663,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167593
167663
  const toId = match ? nodeIdOf(match.m) : void 0;
167594
167664
  if (fromId && toId) {
167595
167665
  edges.push({ id: `e${e++}`, from: fromId, to: toId, kind: "message", label: payload, source: sourceOf2(s.m, uri) });
167666
+ registerDrawn(s.m);
167596
167667
  }
167597
167668
  }
167598
167669
  this.emitBehaviorDefinitions([
@@ -167607,6 +167678,23 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167607
167678
  bare.notes = ["No actions found to flow."];
167608
167679
  return bare;
167609
167680
  }
167681
+ if (drawnRowKeys.size) {
167682
+ for (const element of [...frames, ...nodes]) {
167683
+ const full = element.compartments;
167684
+ if (!full?.length)
167685
+ continue;
167686
+ const kept = full.map((compartment) => ({
167687
+ title: compartment.title,
167688
+ items: compartment.items.filter((row) => {
167689
+ const key = rowKey(row.source);
167690
+ return !key || !drawnRowKeys.has(key);
167691
+ })
167692
+ })).filter((compartment) => compartment.items.length > 0);
167693
+ if (kept.length === full.length && kept.every((c, at) => c.items.length === full[at].items.length))
167694
+ continue;
167695
+ element.compartments = kept.length ? kept : void 0;
167696
+ }
167697
+ }
167610
167698
  const model = this.model("afv", anchor, nodes, edges);
167611
167699
  model.frames = frames;
167612
167700
  model.meta = { ...model.meta, unified: true, groupMode: "nested" };
@@ -167625,7 +167713,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167625
167713
  const def = this.resolveType(holder, index2);
167626
167714
  return def && def !== holder && def.isDef === true ? def : void 0;
167627
167715
  };
167628
- const ownStates = (holder) => membersOf(holder).filter((member) => isStateDecl(member) && nameOf2(member) !== void 0);
167716
+ const ownStates = (holder) => membersOf(holder).map((member) => this.thenActionMemberNode(member) ?? member).filter((member) => isStateDecl(member) && nameOf2(member) !== void 0);
167629
167717
  const nestedStates = (holder) => {
167630
167718
  const out = ownStates(holder);
167631
167719
  const def = definitionOf(holder);
@@ -167664,9 +167752,28 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167664
167752
  drawnStates.push({ node: anchor, id: anchorFrameId });
167665
167753
  idByState.set(anchor, anchorFrameId);
167666
167754
  }
167755
+ const drawnRowKeys = /* @__PURE__ */ new Set();
167756
+ const rowKey = (source) => source ? `${source.uri} ${source.range.start.line} ${source.range.start.character}` : void 0;
167757
+ const registerDrawn = (member) => {
167758
+ const key = rowKey(sourceOf2(member, uri));
167759
+ if (key)
167760
+ drawnRowKeys.add(key);
167761
+ };
167762
+ const namesByFrame = /* @__PURE__ */ new Map();
167763
+ const namesIn = (frameId) => {
167764
+ const key = frameId ?? "";
167765
+ const existing = namesByFrame.get(key);
167766
+ if (existing)
167767
+ return existing;
167768
+ const created = /* @__PURE__ */ new Map();
167769
+ namesByFrame.set(key, created);
167770
+ return created;
167771
+ };
167667
167772
  const renderState = (s, parentFrame, depth, seen) => {
167668
167773
  const id2 = qnameOf(s) || nameOf2(s);
167669
167774
  byName.set(nameOf2(s), id2);
167775
+ namesIn(parentFrame).set(nameOf2(s), id2);
167776
+ registerDrawn(s);
167670
167777
  drawnStates.push({ node: s, id: id2 });
167671
167778
  idByState.set(s, id2);
167672
167779
  const def = s.isDef === true ? void 0 : this.resolveType(s, index2);
@@ -167704,6 +167811,75 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167704
167811
  };
167705
167812
  for (const s of topStates)
167706
167813
  renderState(s, anchorFrameId, 0, /* @__PURE__ */ new Set());
167814
+ const endpointName = (path10) => typeof path10 === "string" ? path10.split(/::|\./).pop() || void 0 : void 0;
167815
+ const controlNodeInfo = (m) => {
167816
+ switch (m.$type) {
167817
+ case "ActionDecl":
167818
+ return m.isDef === true || !nameOf2(m) ? void 0 : { shape: "action", label: nameOf2(m) };
167819
+ // A `perform` names the action it performs, which for a feature
167820
+ // chain (`perform machine.selfTest;`) is the LAST segment.
167821
+ case "PerformStmt": {
167822
+ const segments = [nameOf2(m), ...m.chainSegs ?? []].filter((segment) => !!segment);
167823
+ return { shape: "action", label: segments.at(-1) ?? typeText(m) ?? "perform" };
167824
+ }
167825
+ // User direction 2026-08-30: fork and join only. They express
167826
+ // concurrency, which a transition cannot say: nothing about a
167827
+ // guard or a trigger splits control into parallel regions or
167828
+ // gathers it again. `decide` and `merge` are dropped because a
167829
+ // branch between states IS a guarded transition, which this
167830
+ // view already draws and labels, so a diamond would say the
167831
+ // same thing a second time in the action-flow idiom.
167832
+ // `terminate` is dropped with them: the node carries no name,
167833
+ // so no succession can reach it and it could only ever stand
167834
+ // alone. All three remain in the OMG 8.2.3.18 node set.
167835
+ case "JoinNode":
167836
+ return { shape: "join", label: nameOf2(m) ?? "" };
167837
+ case "ForkNode":
167838
+ return { shape: "fork", label: nameOf2(m) ?? "" };
167839
+ default:
167840
+ return void 0;
167841
+ }
167842
+ };
167843
+ const isInitialMarker = (member) => {
167844
+ const entry = member;
167845
+ return !!entry && entry.$type === "EntryAction" && !entry.name && !entry.actionKw && !entry.send && !entry.accept && !entry.assign;
167846
+ };
167847
+ const machineNodeIds = /* @__PURE__ */ new Map();
167848
+ const machineNodeMembers = [];
167849
+ let anonymousNodes = 0;
167850
+ const emitMachineNodes = (holder, frameId) => {
167851
+ const members = membersOf(holder);
167852
+ const drawnHere = namesIn(frameId);
167853
+ for (const raw of members) {
167854
+ const member = this.thenActionMemberNode(raw) ?? raw;
167855
+ const info = controlNodeInfo(member);
167856
+ if (!info)
167857
+ continue;
167858
+ const name = nameOf2(member);
167859
+ if (name && drawnHere.has(name))
167860
+ continue;
167861
+ const id2 = name ? qnameOf(member) || `${frameId}::${name}` : `${frameId}::__stv_${member.$type}_${anonymousNodes++}__`;
167862
+ if (nodes.some((n2) => n2.id === id2) || frames.some((f) => f.id === id2))
167863
+ continue;
167864
+ if (name) {
167865
+ drawnHere.set(name, id2);
167866
+ if (!byName.has(name))
167867
+ byName.set(name, id2);
167868
+ }
167869
+ machineNodeIds.set(member, id2);
167870
+ machineNodeMembers.push({ member, id: id2 });
167871
+ nodes.push({
167872
+ id: id2,
167873
+ name: info.label,
167874
+ keyword: keywordFor(member),
167875
+ isDef: false,
167876
+ shape: info.shape,
167877
+ compartments: this.compartmentsFor(member, uri, index2),
167878
+ frame: frameId,
167879
+ source: sourceOf2(member, uri)
167880
+ });
167881
+ }
167882
+ };
167707
167883
  const edges = [];
167708
167884
  let e = 0;
167709
167885
  const resolveState = (nm) => {
@@ -167719,53 +167895,201 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167719
167895
  }
167720
167896
  return void 0;
167721
167897
  };
167722
- const transitionScope = [anchor, ...definitionOf(anchor) ? [definitionOf(anchor)] : []];
167723
- for (const t of transitionScope.flatMap((holder) => [...ast_utils_exports.streamAllContents(holder)])) {
167724
- if (t.$type !== "TransitionDecl")
167725
- continue;
167726
- const tn = t;
167727
- const from = resolveState(tn.from);
167728
- const to = resolveState(tn.to);
167898
+ const drawnEdges = /* @__PURE__ */ new Set();
167899
+ const stateLike = /* @__PURE__ */ new Set(["state", "initial", "final"]);
167900
+ const isStateEndpoint = (id2) => {
167901
+ const node = nodes.find((n2) => n2.id === id2);
167902
+ return node ? stateLike.has(node.shape) : frames.some((f) => f.id === id2);
167903
+ };
167904
+ const link = (from, to, label, src) => {
167729
167905
  if (!from || !to)
167730
- continue;
167906
+ return;
167907
+ const kind = isStateEndpoint(from) && isStateEndpoint(to) ? "transition" : "succession";
167908
+ const key = `${from}\0${to}\0${label ?? ""}`;
167909
+ if (drawnEdges.has(key))
167910
+ return;
167911
+ drawnEdges.add(key);
167912
+ if (src)
167913
+ registerDrawn(src);
167731
167914
  edges.push({
167732
167915
  id: `e${e++}`,
167733
167916
  from,
167734
167917
  to,
167735
- kind: "transition",
167736
- label: this.transitionLabel(t),
167737
- source: sourceOf2(t, uri)
167918
+ kind,
167919
+ label,
167920
+ source: src ? sourceOf2(src, uri) : void 0
167738
167921
  });
167922
+ };
167923
+ const guardText = (guard) => {
167924
+ const text = guard?.$cstNode?.text?.replace(/\s+/g, " ").trim();
167925
+ return text ? `[${text}]` : void 0;
167926
+ };
167927
+ const resolveEndpoint = (path10, initialId, frameId) => {
167928
+ if (typeof path10 !== "string")
167929
+ return void 0;
167930
+ const short = endpointName(path10);
167931
+ const local = namesByFrame.get(frameId ?? "");
167932
+ return (short ? local?.get(short) : void 0) ?? resolveState(path10) ?? resolveState(short) ?? (short === "start" ? initialId : void 0);
167933
+ };
167934
+ const transitionScope = [anchor, ...definitionOf(anchor) ? [definitionOf(anchor)] : []];
167935
+ for (const t of transitionScope.flatMap((holder) => [...ast_utils_exports.streamAllContents(holder)])) {
167936
+ if (t.$type !== "TransitionDecl")
167937
+ continue;
167938
+ const tn = t;
167939
+ link(resolveState(tn.from), resolveState(tn.to), this.transitionLabel(t), t);
167739
167940
  }
167740
- const designatedInitial = (owner) => {
167941
+ const designatedInitial = (owner, frameId) => {
167741
167942
  const members = owner.members;
167742
167943
  if (!Array.isArray(members))
167743
167944
  return void 0;
167744
- for (let i = 0; i < members.length - 1; i++) {
167745
- const entry = members[i];
167746
- if (entry.$type !== "EntryAction" || entry.name || entry.actionKw || entry.send || entry.accept || entry.assign)
167945
+ for (let i = 0; i < members.length; i++) {
167946
+ const member = members[i];
167947
+ if (member.$type === "FirstThenChain") {
167948
+ const chain = member;
167949
+ if (endpointName(chain.head) !== "start")
167950
+ continue;
167951
+ const target2 = resolveEndpoint((chain.steps ?? [])[0], void 0, frameId);
167952
+ if (target2) {
167953
+ registerDrawn(member);
167954
+ return target2;
167955
+ }
167956
+ continue;
167957
+ }
167958
+ if (!isInitialMarker(member))
167747
167959
  continue;
167748
167960
  const then = members[i + 1];
167749
167961
  if (then?.$type !== "ThenActionMember" || typeof then.target !== "string")
167750
167962
  continue;
167751
167963
  const target = resolveState(lastSeg(then.target));
167752
- if (target)
167964
+ if (target) {
167965
+ registerDrawn(then);
167753
167966
  return target;
167967
+ }
167754
167968
  }
167755
167969
  return void 0;
167756
167970
  };
167757
- const anchorDefinition = definitionOf(anchor);
167758
- const firstStateId = designatedInitial(anchor) ?? (anchorDefinition ? designatedInitial(anchorDefinition) : void 0) ?? (topStates.length ? byName.get(nameOf2(topStates[0])) : void 0);
167759
- if (firstStateId) {
167760
- edges.push({ id: `e${e++}`, from: "__initial__", to: firstStateId, kind: "transition" });
167971
+ const machineScope = (holder) => {
167972
+ const def = definitionOf(holder);
167973
+ return def ? [holder, def] : [holder];
167974
+ };
167975
+ if (anchorFrameId) {
167976
+ for (const holder of machineScope(anchor))
167977
+ emitMachineNodes(holder, anchorFrameId);
167978
+ }
167979
+ for (const { state, frameId } of compositeFrames) {
167980
+ for (const holder of machineScope(state))
167981
+ emitMachineNodes(holder, frameId);
167761
167982
  }
167983
+ const anchorDefinition = definitionOf(anchor);
167984
+ const firstStateId = designatedInitial(anchor, anchorFrameId) ?? (anchorDefinition ? designatedInitial(anchorDefinition, anchorFrameId) : void 0) ?? (topStates.length ? byName.get(nameOf2(topStates[0])) : void 0);
167985
+ if (firstStateId)
167986
+ link("__initial__", firstStateId, void 0, void 0);
167987
+ const compositeInitial = /* @__PURE__ */ new Map();
167762
167988
  for (const { state, frameId } of compositeFrames) {
167763
- const target = designatedInitial(state);
167989
+ const target = designatedInitial(state, frameId);
167764
167990
  if (!target)
167765
167991
  continue;
167766
167992
  const id2 = `__initial__${frameId}`;
167767
167993
  nodes.push({ id: id2, name: "", keyword: "initial", isDef: false, shape: "initial", frame: frameId });
167768
- edges.push({ id: `e${e++}`, from: id2, to: target, kind: "transition" });
167994
+ compositeInitial.set(state, id2);
167995
+ link(id2, target, void 0, void 0);
167996
+ }
167997
+ const sequentialTransitions = (holder, initialId, frameId) => {
167998
+ let cursor;
167999
+ const idOf = (member) => machineNodeIds.get(member) ?? idByState.get(member) ?? (nameOf2(member) ? resolveEndpoint(nameOf2(member), void 0, frameId) : void 0);
168000
+ const endpoint = (path10) => resolveEndpoint(path10, initialId, frameId);
168001
+ for (const member of membersOf(holder)) {
168002
+ switch (member.$type) {
168003
+ case "StateDecl": {
168004
+ cursor = idOf(member) ?? cursor;
168005
+ break;
168006
+ }
168007
+ // `entry;` IS the start pseudostate, so the succession written
168008
+ // after it leaves that pseudostate rather than the last state.
168009
+ case "EntryAction": {
168010
+ if (isInitialMarker(member)) {
168011
+ cursor = initialId;
168012
+ break;
168013
+ }
168014
+ cursor = machineNodeIds.get(member) ?? cursor;
168015
+ break;
168016
+ }
168017
+ // OMG 8.2.3.18 names this edge `st-succession`, so the
168018
+ // keyworded form is a first-class state-transition edge,
168019
+ // not action-language sugar the state view may skip.
168020
+ // Its ends are ConnectorEnds, not bare paths.
168021
+ case "SuccessionStmt": {
168022
+ const succession = member;
168023
+ let previous = endpoint(this.connectorEndPath(succession.first));
168024
+ let firstHop = true;
168025
+ for (const step of succession.steps ?? []) {
168026
+ const to = endpoint(this.connectorEndPath(step));
168027
+ link(previous, to, firstHop ? guardText(succession.guard) : void 0, member);
168028
+ previous = to;
168029
+ firstHop = false;
168030
+ }
168031
+ if (previous)
168032
+ cursor = previous;
168033
+ break;
168034
+ }
168035
+ case "FirstThenChain": {
168036
+ const chain = member;
168037
+ let previous = endpoint(chain.head);
168038
+ let firstHop = true;
168039
+ for (const step of chain.steps ?? []) {
168040
+ const to = endpoint(step);
168041
+ link(previous, to, firstHop ? guardText(chain.guard) : void 0, member);
168042
+ previous = to;
168043
+ firstHop = false;
168044
+ }
168045
+ if (previous)
168046
+ cursor = previous;
168047
+ break;
168048
+ }
168049
+ case "AcceptNode": {
168050
+ const accept = member;
168051
+ if (typeof accept.thenTarget !== "string")
168052
+ break;
168053
+ link(cursor, endpoint(accept.thenTarget), this.acceptShorthandLabel(member), member);
168054
+ break;
168055
+ }
168056
+ case "ThenActionMember": {
168057
+ const then = member;
168058
+ const declared = this.thenActionMemberNode(member);
168059
+ const to = (declared ? idOf(declared) : void 0) ?? endpoint(then.target);
168060
+ link(cursor, to, void 0, member);
168061
+ if (to)
168062
+ cursor = to;
168063
+ break;
168064
+ }
168065
+ // issue #113: a guarded or default target succession fans out
168066
+ // of the SAME source (a decision node, normally), so neither
168067
+ // branch may advance the cursor past its siblings.
168068
+ case "IfActionNode": {
168069
+ const branch = member;
168070
+ if (typeof branch.thenTarget !== "string")
168071
+ break;
168072
+ link(cursor, endpoint(branch.thenTarget), guardText(branch.cond), member);
168073
+ break;
168074
+ }
168075
+ case "ElseSuccessionMember": {
168076
+ link(cursor, endpoint(member.target), "else", member);
168077
+ break;
168078
+ }
168079
+ default: {
168080
+ cursor = machineNodeIds.get(member) ?? cursor;
168081
+ break;
168082
+ }
168083
+ }
168084
+ }
168085
+ };
168086
+ for (const holder of machineScope(anchor)) {
168087
+ sequentialTransitions(holder, hostsStateMachine ? "__initial__" : void 0, anchorFrameId);
168088
+ }
168089
+ for (const { state, frameId } of compositeFrames) {
168090
+ for (const holder of machineScope(state)) {
168091
+ sequentialTransitions(holder, compositeInitial.get(state), frameId);
168092
+ }
167769
168093
  }
167770
168094
  const exhibitorsByState = /* @__PURE__ */ new Map();
167771
168095
  const exhibitScope = this.packageScopeOf(anchor);
@@ -167847,6 +168171,37 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167847
168171
  edges.push({ id: `e${e++}`, from, to, kind: "causation", label: "\xABcausation\xBB", source: sourceOf2(c, uri) });
167848
168172
  }
167849
168173
  }
168174
+ const wired = /* @__PURE__ */ new Set();
168175
+ for (const edge of edges) {
168176
+ wired.add(edge.from);
168177
+ wired.add(edge.to);
168178
+ }
168179
+ const stranded = new Set(machineNodeMembers.filter(({ id: id2 }) => !wired.has(id2)).map(({ id: id2 }) => id2));
168180
+ if (stranded.size) {
168181
+ for (let at = nodes.length - 1; at >= 0; at--) {
168182
+ if (stranded.has(nodes[at].id))
168183
+ nodes.splice(at, 1);
168184
+ }
168185
+ }
168186
+ for (const { member, id: id2 } of machineNodeMembers) {
168187
+ if (!stranded.has(id2))
168188
+ registerDrawn(member);
168189
+ }
168190
+ for (const frame2 of frames) {
168191
+ const full = frame2.compartments;
168192
+ if (!full?.length)
168193
+ continue;
168194
+ const kept = full.map((compartment) => ({
168195
+ title: compartment.title,
168196
+ items: compartment.items.filter((row) => {
168197
+ const key = rowKey(row.source);
168198
+ return !key || !drawnRowKeys.has(key);
168199
+ })
168200
+ })).filter((compartment) => compartment.items.length > 0);
168201
+ if (kept.length === full.length && kept.every((c, at) => c.items.length === full[at].items.length))
168202
+ continue;
168203
+ frame2.compartments = kept.length ? kept : void 0;
168204
+ }
167850
168205
  const model = this.model("stv", anchor, nodes, edges);
167851
168206
  if (frames.length) {
167852
168207
  model.frames = frames;
@@ -170032,9 +170387,25 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
170032
170387
  return item(m, val ? `= ${val}` : "(value)");
170033
170388
  }));
170034
170389
  add("states", usages(isStateDecl).map((m) => item(m)));
170035
- const stateActions = members.map((m) => ({ m, kind: { EntryAction: "entry", DoAction: "do", ExitAction: "exit" }[m.$type] })).filter((x) => !!x.kind);
170036
- add("state actions", stateActions.map(({ m, kind }) => item(m, `${kind} ${nameOf2(m) ?? ""}`.trim())));
170390
+ const isInitialStateMarker = (m, at) => {
170391
+ const entry = m;
170392
+ if (m.$type !== "EntryAction" || entry.name || entry.actionKw || entry.send || entry.accept || entry.assign)
170393
+ return false;
170394
+ const next = members[at + 1];
170395
+ return next?.$type === "ThenActionMember" || next?.$type === "FirstThenChain";
170396
+ };
170397
+ const stateActionTitle = {
170398
+ EntryAction: "entry",
170399
+ DoAction: "during",
170400
+ ExitAction: "exit"
170401
+ };
170402
+ const stateActionText = (m) => nameOf2(m) ?? (m.$cstNode?.text ?? "").replace(/\s+/g, " ").replace(/;$/, "").replace(/^(entry|do|exit)\s+(action\s+)?/, "").trim();
170403
+ for (const title of ["entry", "during", "exit"]) {
170404
+ add(title, members.filter((m, at) => stateActionTitle[m.$type] === title && !isInitialStateMarker(m, at)).map((m) => item(m, stateActionText(m))).filter((row) => !!row.text));
170405
+ }
170037
170406
  add("state transition", members.filter((m) => m.$type === "TransitionDecl").map((m) => item(m, `${m.from ?? ""} \u2192 ${m.to ?? ""}`)));
170407
+ const successionText = (m) => (m.$cstNode?.text ?? "").replace(/\s+/g, " ").replace(/;$/, "").trim();
170408
+ add("successions", members.filter((m) => m.$type === "FirstThenChain" || m.$type === "SuccessionStmt" || m.$type === "ThenActionMember" && typeof m.target === "string" || m.$type === "ElseSuccessionMember" || (m.$type === "AcceptNode" || m.$type === "IfActionNode") && typeof m.thenTarget === "string").map((m) => item(m, successionText(m))).filter((row) => !!row.text));
170038
170409
  add("assert constraints", members.filter((m) => m.$type === "AssertConstraintStmt").map((m) => item(m, exprText3(m) || nameOf2(m) || "assert constraint")));
170039
170410
  add("invariants", members.filter((m) => m.$type === "InvShorthand").map((m) => {
170040
170411
  const inv = m;
@@ -170043,6 +170414,31 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
170043
170414
  return item(m, `${head2} { ${expr} }`.replace(/\s+/g, " ").trim());
170044
170415
  }));
170045
170416
  add("actions", usages(isActionDecl).map((m) => item(m)));
170417
+ const performRowText = (m) => {
170418
+ const referenced = this.behaviorReferenceText(m);
170419
+ if (this.isAnonymousBehaviorReference(m))
170420
+ return referenced || this.featureText(m);
170421
+ const declared = this.featureText(m);
170422
+ return referenced && referenced !== nameOf2(m) ? `${declared} references ${referenced}` : declared;
170423
+ };
170424
+ add("perform actions", members.filter((m) => m.$type === "PerformStmt").filter(isNonVariantMember).map((m) => item(m, performRowText(m))));
170425
+ if (isActionFlowStep(node)) {
170426
+ const scope = qnameOf(this.packageScopeOf(node));
170427
+ const performerName = (part) => {
170428
+ const qualified = qnameOf(part);
170429
+ if (!qualified)
170430
+ return nameOf2(part) ?? "?";
170431
+ return scope && qualified.startsWith(`${scope}::`) ? qualified.slice(scope.length + 2) : qualified;
170432
+ };
170433
+ const claims = this.performClaimsByTarget(this.documentRootOf(node), index2).get(node) ?? [];
170434
+ const seenPerformers = /* @__PURE__ */ new Set();
170435
+ add("performed by", claims.map((claim) => ({ claim, part: this.declaringOwnerOf(claim) })).filter((entry) => !!entry.part && isPartDecl(entry.part)).map(({ claim, part }) => ({ text: performerName(part), source: sourceOf2(claim, uri) })).filter((row) => {
170436
+ if (seenPerformers.has(row.text))
170437
+ return false;
170438
+ seenPerformers.add(row.text);
170439
+ return true;
170440
+ }));
170441
+ }
170046
170442
  add("concerns", usages(isConcernDecl).map((m) => item(m)));
170047
170443
  add("viewpoints", usages(isViewpointDecl).map((m) => item(m)));
170048
170444
  add("exposes", members.filter((m) => m.$type === "ExposeMember").flatMap((m) => (m.paths ?? []).map((path10) => item(path10, this.featurePath(path10) ?? (path10.$cstNode?.text ?? "").replace(/\s+/g, " ").trim()))));
@@ -170125,9 +170521,19 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
170125
170521
  // REQ-186/REQ-198 — transition label: `trigger [guard] / effect`.
170126
170522
  transitionLabel(t) {
170127
170523
  const tn = t;
170128
- const trigger = this.acceptTriggerLabel(tn.accept);
170129
- const guard = tn.guard?.$cstNode?.text?.replace(/\s+/g, " ").trim();
170130
- const effect = this.effectLabel(tn.effect);
170524
+ return this.transitionLabelOf(tn.accept, tn.guard, tn.effect);
170525
+ }
170526
+ // REQ-198: the label of a source-less transition shorthand (`accept Sig if g
170527
+ // do effect then t;`, OMG SysML 8.2.2.18.3). The accept node itself carries
170528
+ // the trigger, so it reads exactly like an explicit `transition`.
170529
+ acceptShorthandLabel(accept) {
170530
+ const node = accept;
170531
+ return this.transitionLabelOf(accept, node.guard, node.effect);
170532
+ }
170533
+ transitionLabelOf(accept, guardExpr, effectClause) {
170534
+ const trigger = this.acceptTriggerLabel(accept);
170535
+ const guard = guardExpr?.$cstNode?.text?.replace(/\s+/g, " ").trim();
170536
+ const effect = this.effectLabel(effectClause);
170131
170537
  const parts = [];
170132
170538
  if (trigger)
170133
170539
  parts.push(String(trigger));
@@ -196775,7 +197181,7 @@ var AFV_LAYOUT_EDGE_KINDS = /* @__PURE__ */ new Set([
196775
197181
  "succession",
196776
197182
  "successionFlow"
196777
197183
  ]);
196778
- var STV_LAYOUT_EDGE_KINDS = /* @__PURE__ */ new Set(["transition"]);
197184
+ var STV_LAYOUT_EDGE_KINDS = /* @__PURE__ */ new Set(["transition", "succession"]);
196779
197185
  function behaviorLayoutEdgeKinds(kind) {
196780
197186
  return kind === "afv" ? AFV_LAYOUT_EDGE_KINDS : kind === "stv" ? STV_LAYOUT_EDGE_KINDS : void 0;
196781
197187
  }
@@ -201984,6 +202390,10 @@ var TOOLBOX = {
201984
202390
  ],
201985
202391
  relations: [
201986
202392
  rel("transition", "transition", "\u2192", "click the source state, then the target state"),
202393
+ // issue #113 - a machine's action and control nodes are sequenced by
202394
+ // a succession, not by a transition: `transition` needs a state at
202395
+ // both ends (OMG SysML 8.2.2.18.3), while `first a then b` does not.
202396
+ rel("succession", "succession", "\u21E2", "click the source node, then the target node"),
201987
202397
  // issue #115 — nothing could mark the initial state, so a state
201988
202398
  // machine could not be STARTED from a blank canvas. The gesture runs
201989
202399
  // from the always-drawn initial pseudostate to the first state.
@@ -202142,7 +202552,11 @@ function normalizedElementKeyword(keyword) {
202142
202552
  "loop action",
202143
202553
  "for-loop action",
202144
202554
  "perform action",
202145
- "assign"
202555
+ "assign",
202556
+ // issue #113 — a state sub-action drawn as a node is an action node.
202557
+ "entry action",
202558
+ "do action",
202559
+ "exit action"
202146
202560
  ].includes(base)) {
202147
202561
  base = "action";
202148
202562
  } else if (["fork", "join", "merge", "decide", "decision"].includes(base)) {
@@ -202244,7 +202658,12 @@ function relationToolsForNode(keyword, viewKind, shape) {
202244
202658
  if (base === "port") return false;
202245
202659
  return base === "pin" && (tool.kind === "flow" || tool.kind === "successionFlow" || tool.kind === "bind");
202246
202660
  }
202247
- if (viewKind === "stv") return base === "state" && tool.kind !== "initial";
202661
+ if (viewKind === "stv") {
202662
+ if (base === "action" || base === "control") {
202663
+ return tool.kind === "succession" || tool.kind === TERMINAL_RELATION;
202664
+ }
202665
+ return base === "state" && tool.kind !== "initial";
202666
+ }
202248
202667
  if (viewKind === "sv") return shape === "lifeline" && (base === "part" || base === "lifeline");
202249
202668
  if (viewKind === "cv") {
202250
202669
  if (base === "actor") return tool.kind === "actor";
@@ -204707,7 +205126,7 @@ async function runExport(command) {
204707
205126
  }
204708
205127
 
204709
205128
  // src/main.ts
204710
- var VERSION2 = true ? "0.25.0" : "dev";
205129
+ var VERSION2 = true ? "0.27.0" : "dev";
204711
205130
  function display(file) {
204712
205131
  const rel2 = path9.relative(process.cwd(), file);
204713
205132
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;