@mmmnt/cli 0.8.0 → 0.9.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/dist/moment.mjs CHANGED
@@ -59218,6 +59218,10 @@ var MomentGrammar = () => loadedMomentGrammar ?? (loadedMomentGrammar = loadGram
59218
59218
  {
59219
59219
  "$type": "Keyword",
59220
59220
  "value": "note"
59221
+ },
59222
+ {
59223
+ "$type": "Keyword",
59224
+ "value": "outcome"
59221
59225
  }
59222
59226
  ]
59223
59227
  },
@@ -59324,6 +59328,10 @@ var MomentGrammar = () => loadedMomentGrammar ?? (loadedMomentGrammar = loadGram
59324
59328
  {
59325
59329
  "$type": "Keyword",
59326
59330
  "value": "terminal"
59331
+ },
59332
+ {
59333
+ "$type": "Keyword",
59334
+ "value": "outcome"
59327
59335
  }
59328
59336
  ]
59329
59337
  },
@@ -59765,6 +59773,10 @@ var MomentGrammar = () => loadedMomentGrammar ?? (loadedMomentGrammar = loadGram
59765
59773
  {
59766
59774
  "$type": "Keyword",
59767
59775
  "value": "note"
59776
+ },
59777
+ {
59778
+ "$type": "Keyword",
59779
+ "value": "outcome"
59768
59780
  }
59769
59781
  ]
59770
59782
  },
@@ -60291,7 +60303,7 @@ var MomentValidator = class {
60291
60303
  // those are alternative branch arms, not continuations.
60292
60304
  // =========================================================================
60293
60305
  checkTerminalIsLast(node, accept) {
60294
- if (node.modifier?.type !== "terminal")
60306
+ if (!endsFlow(node))
60295
60307
  return;
60296
60308
  const container = node.$container;
60297
60309
  const siblings = "nodes" in container ? container.nodes : [];
@@ -60762,6 +60774,10 @@ var MomentValidator = class {
60762
60774
  return false;
60763
60775
  }
60764
60776
  };
60777
+ function endsFlow(node) {
60778
+ const type = node.modifier?.type;
60779
+ return type === "terminal" || type === "outcome";
60780
+ }
60765
60781
 
60766
60782
  // ../core/dist/moment-module.js
60767
60783
  var MomentModule = {
@@ -61778,13 +61794,18 @@ function transformNodeToEntry(node, laneContextMap, nodeKindMap) {
61778
61794
  if (node.cause !== void 0) {
61779
61795
  entry.cause = node.cause;
61780
61796
  }
61781
- const modifierType = node.modifier?.type;
61797
+ applyModifier(entry, node.modifier?.type);
61798
+ return entry;
61799
+ }
61800
+ function applyModifier(entry, modifierType) {
61782
61801
  if (modifierType === "optional") {
61783
61802
  entry.optional = true;
61784
61803
  } else if (modifierType === "terminal") {
61785
61804
  entry.terminal = true;
61805
+ } else if (modifierType === "outcome") {
61806
+ entry.terminal = true;
61807
+ entry.exit = "outcome";
61786
61808
  }
61787
- return entry;
61788
61809
  }
61789
61810
  function extractConnections(flow, laneContextMap) {
61790
61811
  const connections = [];
@@ -64934,14 +64955,16 @@ function resolveScenarioLabel(flow, reachedBranches) {
64934
64955
  if (deviations.length === 0)
64935
64956
  return `Happy Path: ${flow.name}`;
64936
64957
  const bracket = deviations.map((b) => b.condition).join(", ");
64937
- const hasTerminalArm = deviations.some((b) => b.armHasTerminal);
64938
- return hasTerminalArm ? `Failure Path: ${flow.name} [${bracket}]` : `Variant: ${flow.name} [${bracket}]`;
64958
+ return endsAtFailureExit(deviations) ? `Failure Path: ${flow.name} [${bracket}]` : `Variant: ${flow.name} [${bracket}]`;
64939
64959
  }
64940
64960
  function resolveScenarioKind(reachedBranches) {
64941
64961
  const deviations = reachedBranches.filter((b) => !b.isFirstArm);
64942
64962
  if (deviations.length === 0)
64943
64963
  return "happy";
64944
- return deviations.some((b) => b.armHasTerminal) ? "failure" : "variant";
64964
+ return endsAtFailureExit(deviations) ? "failure" : "variant";
64965
+ }
64966
+ function endsAtFailureExit(deviations) {
64967
+ return deviations.some((b) => b.armHasFailureExit);
64945
64968
  }
64946
64969
  function buildContextMap(ir) {
64947
64970
  return new Map(ir.contexts.map((c) => [c.id, c]));
@@ -65021,6 +65044,10 @@ function selectBranch(moment, branchSelections, state) {
65021
65044
  const branch = (selected ? branches.find((b) => b.condition === selected) : void 0) ?? branches[0];
65022
65045
  return { branch, forced };
65023
65046
  }
65047
+ function armEndsAtFailureExit(branch) {
65048
+ const exit = branch.entries.find((e) => e.terminal);
65049
+ return exit !== void 0 && exit.exit !== "outcome";
65050
+ }
65024
65051
  function recordBranchPoint(state, moment, branch, branchIdx, forced) {
65025
65052
  state.activations.push({ momentName: moment.name, condition: branch.condition });
65026
65053
  if (!forced && !state.reachedNames.has(moment.name)) {
@@ -65029,7 +65056,7 @@ function recordBranchPoint(state, moment, branch, branchIdx, forced) {
65029
65056
  momentName: moment.name,
65030
65057
  condition: branch.condition,
65031
65058
  isFirstArm: branchIdx === 0,
65032
- armHasTerminal: branch.entries.some((e) => e.terminal)
65059
+ armHasFailureExit: armEndsAtFailureExit(branch)
65033
65060
  });
65034
65061
  }
65035
65062
  }
@@ -65278,23 +65305,40 @@ function findEventAfter(events, eventType, afterIndex) {
65278
65305
  function deriveNegativeScenarios(ir, flow, options) {
65279
65306
  const contextMap = buildContextMap(ir);
65280
65307
  const opts = resolveOptions(flow, options);
65281
- const walk = walkPath(flow, opts.branchSelections);
65282
- const nodes = walk.nodes;
65283
65308
  const scenarios = [];
65284
65309
  const usedIds = /* @__PURE__ */ new Set();
65285
- for (let i = 0; i < nodes.length; i++) {
65286
- const node = nodes[i];
65287
- const kind = resolveNodeKind(node.nodeName, node.contextId, contextMap, node.nodeKind);
65288
- if (kind !== "command")
65289
- continue;
65290
- const preconditions = findPreconditions(node, contextMap);
65291
- for (const pre of preconditions) {
65292
- const baseId = `${opts.scenarioId}-neg-${node.nodeName}-${pre.name}`;
65293
- const scenarioId = usedIds.has(baseId) ? `${baseId}-${i}` : baseId;
65294
- usedIds.add(scenarioId);
65295
- usedIds.add(baseId);
65296
- scenarios.push(buildNegativeScenario(ir, flow, walk, i, pre, contextMap, opts, scenarioId));
65310
+ const covered = /* @__PURE__ */ new Set();
65311
+ const deriveOnPath = (walk, onlyUncovered) => {
65312
+ const nodes = walk.nodes;
65313
+ for (let i = 0; i < nodes.length; i++) {
65314
+ const node = nodes[i];
65315
+ const kind = resolveNodeKind(node.nodeName, node.contextId, contextMap, node.nodeKind);
65316
+ if (kind !== "command")
65317
+ continue;
65318
+ for (const pre of findPreconditions(node, contextMap)) {
65319
+ const key = [node.nodeName, pre.name, node.cause ?? ""].join("\0");
65320
+ if (onlyUncovered && covered.has(key))
65321
+ continue;
65322
+ covered.add(key);
65323
+ const baseId = `${opts.scenarioId}-neg-${node.nodeName}-${pre.name}`;
65324
+ const scenarioId = usedIds.has(baseId) ? `${baseId}-${i}` : baseId;
65325
+ usedIds.add(scenarioId);
65326
+ usedIds.add(baseId);
65327
+ scenarios.push(buildNegativeScenario(ir, flow, walk, i, pre, contextMap, opts, scenarioId));
65328
+ }
65297
65329
  }
65330
+ };
65331
+ deriveOnPath(walkPath(flow, opts.branchSelections), false);
65332
+ if (options?.branchSelections !== void 0)
65333
+ return scenarios;
65334
+ const seenPaths = /* @__PURE__ */ new Set();
65335
+ for (const selections of enumerateBranchCombinations(flow)) {
65336
+ const walk = walkPath(flow, selections);
65337
+ const pathKey = walk.nodes.map((n) => n.nodeId).join("|");
65338
+ if (seenPaths.has(pathKey))
65339
+ continue;
65340
+ seenPaths.add(pathKey);
65341
+ deriveOnPath(walk, true);
65298
65342
  }
65299
65343
  return scenarios;
65300
65344
  }
@@ -66253,46 +66297,51 @@ function findProcess(nodeName, ctx) {
66253
66297
  return null;
66254
66298
  }
66255
66299
  function buildConnections(flow) {
66256
- return flow.connections.map((conn) => {
66257
- const eventType = conn.eventId.replace(/^evt-/, "");
66258
- const endpoints = {
66259
- eventType,
66260
- sourceNodeName: conn.sourceNodeName,
66261
- targetNodeName: conn.targetNodeName
66262
- };
66263
- if (conn.connectionType === "returns-to") {
66264
- if (conn.targetMomentId) {
66265
- return {
66266
- from: conn.sourceMomentId,
66267
- to: conn.targetMomentId,
66268
- style: "return",
66269
- targetMomentLabel: conn.targetMomentLabel,
66270
- ...endpoints
66271
- };
66272
- }
66300
+ return flow.connections.map((conn) => ({
66301
+ ...buildConnection(conn),
66302
+ // ADR-044: the IR already states which arm declared the connection.
66303
+ ...conn.branchCondition !== void 0 ? { branchCondition: conn.branchCondition } : {}
66304
+ }));
66305
+ }
66306
+ function buildConnection(conn) {
66307
+ const eventType = conn.eventId.replace(/^evt-/, "");
66308
+ const endpoints = {
66309
+ eventType,
66310
+ sourceNodeName: conn.sourceNodeName,
66311
+ targetNodeName: conn.targetNodeName
66312
+ };
66313
+ if (conn.connectionType === "returns-to") {
66314
+ if (conn.targetMomentId) {
66273
66315
  return {
66274
66316
  from: conn.sourceMomentId,
66275
- to: conn.targetContextId,
66276
- label: `unresolved returns-to target: ${conn.targetMomentLabel ?? "(unknown)"}`,
66317
+ to: conn.targetMomentId,
66277
66318
  style: "return",
66278
66319
  targetMomentLabel: conn.targetMomentLabel,
66279
66320
  ...endpoints
66280
66321
  };
66281
66322
  }
66282
- const isCrossing2 = conn.connectionType === "crosses-to";
66283
66323
  return {
66284
66324
  from: conn.sourceMomentId,
66285
- // For 'triggers' the core now resolves targetContextId to the context
66286
- // where the TARGET node is placed (not the source's own lane).
66287
66325
  to: conn.targetContextId,
66288
- label: isCrossing2 ? eventType : void 0,
66289
- style: mapConnectionStyle(conn),
66290
- crossBoundary: isCrossing2 || void 0,
66291
- relationship: isCrossing2 ? conn.schemaContract.relationshipType : void 0,
66292
- schemaContract: isCrossing2 ? mapSchemaContract(conn) : void 0,
66326
+ label: `unresolved returns-to target: ${conn.targetMomentLabel ?? "(unknown)"}`,
66327
+ style: "return",
66328
+ targetMomentLabel: conn.targetMomentLabel,
66293
66329
  ...endpoints
66294
66330
  };
66295
- });
66331
+ }
66332
+ const isCrossing2 = conn.connectionType === "crosses-to";
66333
+ return {
66334
+ from: conn.sourceMomentId,
66335
+ // For 'triggers' the core now resolves targetContextId to the context
66336
+ // where the TARGET node is placed (not the source's own lane).
66337
+ to: conn.targetContextId,
66338
+ label: isCrossing2 ? eventType : void 0,
66339
+ style: mapConnectionStyle(conn),
66340
+ crossBoundary: isCrossing2 || void 0,
66341
+ relationship: isCrossing2 ? conn.schemaContract.relationshipType : void 0,
66342
+ schemaContract: isCrossing2 ? mapSchemaContract(conn) : void 0,
66343
+ ...endpoints
66344
+ };
66296
66345
  }
66297
66346
  function mapConnectionStyle(conn) {
66298
66347
  if (conn.connectionType === "crosses-to")
@@ -66565,12 +66614,16 @@ function renderNoteOnlyMoment(lines, moment, flow) {
66565
66614
  lines.push(...noteComments(moment, flow, " "));
66566
66615
  lines.push("");
66567
66616
  }
66617
+ function armEndsAtOutcome(branchEntries) {
66618
+ return branchEntries.find((e) => e.terminal)?.exit === "outcome";
66619
+ }
66568
66620
  function renderTerminalBranchScenario(lines, moment, condition, branchEntries, flow, ctxMap, ir) {
66569
- const tags = ["@terminal", "@failure-path"];
66621
+ const endsAtOutcome = armEndsAtOutcome(branchEntries);
66622
+ const tags = ["@terminal", endsAtOutcome ? "@alt-path" : "@failure-path"];
66570
66623
  lines.push(` ${tags.join(" ")}`);
66571
66624
  lines.push(` Scenario: ${moment.name} [${condition}]`);
66572
66625
  lines.push(...noteComments(moment, flow, " "));
66573
- const failedPrecondition = findFailedPrecondition(moment, ctxMap);
66626
+ const failedPrecondition = endsAtOutcome ? void 0 : findFailedPrecondition(moment, ctxMap);
66574
66627
  lines.push(` Given the ${moment.name.toLowerCase()} is evaluated`);
66575
66628
  if (failedPrecondition) {
66576
66629
  lines.push(` When ${failedPrecondition.name} is not satisfied`);
@@ -66585,7 +66638,8 @@ function renderTerminalBranchScenario(lines, moment, condition, branchEntries, f
66585
66638
  lines.push(` Then the flow terminates because ${failedPrecondition.description}`);
66586
66639
  } else {
66587
66640
  const terminalNode = branchEntries.find((e) => e.terminal)?.nodeName;
66588
- lines.push(` Then the flow terminates${terminalNode ? ` at ${terminalNode}` : ""}`);
66641
+ const verb = endsAtOutcome ? "ends" : "terminates";
66642
+ lines.push(` Then the flow ${verb}${terminalNode ? ` at ${terminalNode}` : ""}`);
66589
66643
  }
66590
66644
  const compensation = findCompensationForMoment(moment, ir);
66591
66645
  if (compensation) {
@@ -66714,7 +66768,7 @@ function readOrEmit(entry) {
66714
66768
  return "emits";
66715
66769
  }
66716
66770
  function renderInternalEventStep(lines, entry, evt, ctxName) {
66717
- const modifier = entry.optional ? " (optional)" : entry.terminal ? " (terminal)" : "";
66771
+ const modifier = entry.optional ? " (optional)" : entry.terminal ? entry.exit === "outcome" ? " (outcome)" : " (terminal)" : "";
66718
66772
  lines.push(` Then ${ctxName} ${readOrEmit(entry)} ${entry.nodeName}${modifier}`);
66719
66773
  if (evt && evt.fields.length > 0) {
66720
66774
  lines.push(` carrying ${evt.fields.map((f) => f.name).join(", ")}`);
@@ -67221,8 +67275,10 @@ var SpecificationDocumentGenerator = class {
67221
67275
  }
67222
67276
  renderTerminalBranch(lines, moment, branch, flow, ir) {
67223
67277
  const usedConnections = /* @__PURE__ */ new Set();
67224
- const terminalNode = branch.entries.find((e) => e.terminal)?.nodeName;
67225
- lines.push(` - \u274C **If ${branch.condition}:** flow terminates${terminalNode ? ` at **${terminalNode}**` : ""}`);
67278
+ const exit = branch.entries.find((e) => e.terminal);
67279
+ const terminalNode = exit?.nodeName;
67280
+ const [mark, verb] = exit?.exit === "outcome" ? ["\u2705", "ends"] : ["\u274C", "terminates"];
67281
+ lines.push(` - ${mark} **If ${branch.condition}:** flow ${verb}${terminalNode ? ` at **${terminalNode}**` : ""}`);
67226
67282
  for (const entry of branch.entries) {
67227
67283
  if (entry.terminal)
67228
67284
  continue;
@@ -67548,8 +67604,11 @@ var SpecificationDocumentGenerator = class {
67548
67604
  }
67549
67605
  renderBranchAltBlock(lines, moment, ctxName, flow, ir) {
67550
67606
  const nonTerminal = moment.branches.filter((b) => !b.entries.some((e) => e.terminal));
67551
- const terminal = moment.branches.filter((b) => b.entries.some((e) => e.terminal));
67552
- if (nonTerminal.length === 0 && terminal.length === 0)
67607
+ const ending = moment.branches.filter((b) => b.entries.some((e) => e.terminal));
67608
+ const isOutcome = (b) => b.entries.find((e) => e.terminal)?.exit === "outcome";
67609
+ const terminal = ending.filter((b) => !isOutcome(b));
67610
+ const outcome = ending.filter(isOutcome);
67611
+ if (nonTerminal.length === 0 && ending.length === 0)
67553
67612
  return;
67554
67613
  for (let i = 0; i < nonTerminal.length; i++) {
67555
67614
  const branch = nonTerminal[i];
@@ -67557,11 +67616,16 @@ var SpecificationDocumentGenerator = class {
67557
67616
  lines.push(` ${keyword} ${branch.condition}`);
67558
67617
  this.renderSequenceEntries(lines, branch.entries, moment, branch.condition, flow, ir);
67559
67618
  }
67560
- if (terminal.length > 0) {
67561
- const keyword = nonTerminal.length === 0 ? "alt" : "else";
67562
- const labels = terminal.map((b) => b.condition).join(" / ");
67563
- lines.push(` ${keyword} ${labels}`);
67564
- lines.push(` Note over ${ctxName}: Flow terminates`);
67619
+ let opened = nonTerminal.length > 0;
67620
+ for (const [arms, note] of [
67621
+ [terminal, "Flow terminates"],
67622
+ [outcome, "Flow ends at an outcome"]
67623
+ ]) {
67624
+ if (arms.length === 0)
67625
+ continue;
67626
+ lines.push(` ${opened ? "else" : "alt"} ${arms.map((b) => b.condition).join(" / ")}`);
67627
+ lines.push(` Note over ${ctxName}: ${note}`);
67628
+ opened = true;
67565
67629
  }
67566
67630
  lines.push(" end");
67567
67631
  }