jssm 5.155.1 → 5.156.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/README.md CHANGED
@@ -18,10 +18,10 @@ Please edit the file it's derived from, instead: `./src/md/readme_base.md`
18
18
 
19
19
 
20
20
 
21
- * Generated for version 5.155.1 at 6/28/2026, 7:52:33 PM
21
+ * Generated for version 5.156.0 at 6/28/2026, 9:25:43 PM
22
22
 
23
23
  -->
24
- # jssm 5.155.1
24
+ # jssm 5.156.0
25
25
 
26
26
  [**Try the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·
27
27
  [Documentation](https://stonecypher.github.io/jssm/docs/) ·
@@ -312,7 +312,7 @@ That decision shows up everywhere downstream:
312
312
  or run `npm run benny` against your own machine.
313
313
 
314
314
  - **More thoroughly tested than any other JavaScript state-machine
315
- library.** 7,815 tests at 100.0% line coverage
315
+ library.** 7,822 tests at 100.0% line coverage
316
316
  ([report](https://coveralls.io/github/StoneCypher/jssm)), plus
317
317
  fuzz testing via `fast-check`, with parser test data across ten natural
318
318
  languages and Emoji.
@@ -445,11 +445,11 @@ If your contribution is missing here, please open an issue.
445
445
 
446
446
  <br/>
447
447
 
448
- ***7,815 tests***, run 82,857 times.
448
+ ***7,822 tests***, run 82,864 times.
449
449
 
450
- - 7,057 specs with 100.0% coverage
451
- - 758 fuzz tests with 54.7% coverage
452
- - 9,686 TypeScript lines - 0.8 tests per line, 8.6 generated tests per line
450
+ - 7,064 specs with 100.0% coverage
451
+ - 758 fuzz tests with 54.1% coverage
452
+ - 9,761 TypeScript lines - 0.8 tests per line, 8.5 generated tests per line
453
453
 
454
454
  [![Actions Status](https://github.com/StoneCypher/jssm/workflows/Node%20CI/badge.svg)](https://github.com/StoneCypher/jssm/actions)
455
455
  [![NPM version](https://img.shields.io/npm/v/jssm.svg)](https://www.npmjs.com/package/jssm)
@@ -4543,6 +4543,45 @@
4543
4543
  }
4544
4544
  },
4545
4545
  "description": "Render the current `fsl` source to an SVG string via the headless\r\n`fsl_to_svg_string` pipeline. Updates `_svg` on success; emits a\r\n`viz-error` `CustomEvent` on failure. Guards against stale results\r\nwhen `fsl` changes mid-flight."
4546
+ },
4547
+ {
4548
+ "kind": "method",
4549
+ "name": "clearHighlights",
4550
+ "privacy": "public",
4551
+ "return": {
4552
+ "type": {
4553
+ "text": "void"
4554
+ }
4555
+ },
4556
+ "description": "Clears any active programmatic highlights from the rendered SVG,\r\nrestoring every node and edge to its default Graphviz presentation.\r\n\r\nRemoves only the inline `fill` / `stroke` / `opacity` overrides that\r\nhighlightTrace installs; the SVG's own presentation attributes\r\nare untouched. Safe to call when nothing is highlighted, before the\r\nfirst render, or while detached — it no-ops if there is no rendered\r\nSVG to clear.\r\n\r\n```typescript\r\nconst viz = document.querySelector('fsl-viz');\r\nviz.highlightTrace(['a', 'b']);\r\nviz.clearHighlights(); // back to the default rendering\r\n```"
4557
+ },
4558
+ {
4559
+ "kind": "method",
4560
+ "name": "highlightTrace",
4561
+ "privacy": "public",
4562
+ "return": {
4563
+ "type": {
4564
+ "text": "void"
4565
+ }
4566
+ },
4567
+ "parameters": [
4568
+ {
4569
+ "name": "trace",
4570
+ "type": {
4571
+ "text": "string[]"
4572
+ },
4573
+ "description": "Ordered state names describing the path (e.g.\r\n`['A', 'B', 'C']`). Consecutive pairs select the edges\r\n`A->B` and `B->C`. An empty array is a no-op."
4574
+ },
4575
+ {
4576
+ "name": "options",
4577
+ "default": "{}",
4578
+ "type": {
4579
+ "text": "HighlightOptions"
4580
+ },
4581
+ "description": "Highlight styling; see {@link HighlightOptions}. Defaults\r\n * to crimson with off-trace fading enabled.\r\n *\r\n * "
4582
+ }
4583
+ ],
4584
+ "description": "Programmatically highlights one execution trace (a path of state names)\r\nthrough the rendered graph, optionally fading everything off the path.\r\n\r\nMatches nodes by their Graphviz `<title>` (the state name) and edges by\r\nthe `from->to` title Graphviz emits, applying inline style overrides so\r\nthe highlight composes over — and is reversible against — the default\r\nrendering (see clearHighlights, which this calls first). No-ops\r\nwhen detached, before the first render, or given an empty trace.\r\n\r\n```typescript\r\n// Highlight a -> b -> c in green, without dimming the rest:\r\nviz.highlightTrace(['a', 'b', 'c'], { color: '#2e7d32', fadeOthers: false });\r\n```"
4546
4585
  }
4547
4586
  ],
4548
4587
  "events": [
@@ -23563,7 +23563,7 @@ var constants = /*#__PURE__*/Object.freeze({
23563
23563
  * Useful for runtime diagnostics and for embedding in serialized machine
23564
23564
  * snapshots so that deserializers can detect version-skew.
23565
23565
  */
23566
- const version = "5.155.1";
23566
+ const version = "5.156.0";
23567
23567
 
23568
23568
  // whargarbl lots of these return arrays could/should be sets
23569
23569
  const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
package/dist/cdn/viz.js CHANGED
@@ -23588,7 +23588,7 @@ var constants = /*#__PURE__*/Object.freeze({
23588
23588
  * Useful for runtime diagnostics and for embedding in serialized machine
23589
23589
  * snapshots so that deserializers can detect version-skew.
23590
23590
  */
23591
- const version = "5.155.1";
23591
+ const version = "5.156.0";
23592
23592
 
23593
23593
  // whargarbl lots of these return arrays could/should be sets
23594
23594
  const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
@@ -30372,6 +30372,125 @@ class FslViz extends i$1 {
30372
30372
  render() {
30373
30373
  return b `<div class="container">${o(this._svg)}</div>`;
30374
30374
  }
30375
+ /**
30376
+ * Clears any active programmatic highlights from the rendered SVG,
30377
+ * restoring every node and edge to its default Graphviz presentation.
30378
+ *
30379
+ * Removes only the inline `fill` / `stroke` / `opacity` overrides that
30380
+ * {@link highlightTrace} installs; the SVG's own presentation attributes
30381
+ * are untouched. Safe to call when nothing is highlighted, before the
30382
+ * first render, or while detached — it no-ops if there is no rendered
30383
+ * SVG to clear.
30384
+ *
30385
+ * ```typescript
30386
+ * const viz = document.querySelector('fsl-viz');
30387
+ * viz.highlightTrace(['a', 'b']);
30388
+ * viz.clearHighlights(); // back to the default rendering
30389
+ * ```
30390
+ *
30391
+ * @see highlightTrace
30392
+ */
30393
+ clearHighlights() {
30394
+ if (!this.shadowRoot) {
30395
+ return;
30396
+ }
30397
+ const container = this.shadowRoot.querySelector('.container');
30398
+ if (!container) {
30399
+ return;
30400
+ }
30401
+ // Remove the inline styles that override the SVG presentation attributes.
30402
+ const elements = container.querySelectorAll('.node, .edge, .node *, .edge *');
30403
+ elements.forEach(el => {
30404
+ el.style.removeProperty('fill');
30405
+ el.style.removeProperty('stroke');
30406
+ el.style.removeProperty('opacity');
30407
+ });
30408
+ }
30409
+ /**
30410
+ * Programmatically highlights one execution trace (a path of state names)
30411
+ * through the rendered graph, optionally fading everything off the path.
30412
+ *
30413
+ * Matches nodes by their Graphviz `<title>` (the state name) and edges by
30414
+ * the `from->to` title Graphviz emits, applying inline style overrides so
30415
+ * the highlight composes over — and is reversible against — the default
30416
+ * rendering (see {@link clearHighlights}, which this calls first). No-ops
30417
+ * when detached, before the first render, or given an empty trace.
30418
+ *
30419
+ * ```typescript
30420
+ * // Highlight a -> b -> c in green, without dimming the rest:
30421
+ * viz.highlightTrace(['a', 'b', 'c'], { color: '#2e7d32', fadeOthers: false });
30422
+ * ```
30423
+ *
30424
+ * @param trace Ordered state names describing the path (e.g.
30425
+ * `['A', 'B', 'C']`). Consecutive pairs select the edges
30426
+ * `A->B` and `B->C`. An empty array is a no-op.
30427
+ * @param options Highlight styling; see {@link HighlightOptions}. Defaults
30428
+ * to crimson with off-trace fading enabled.
30429
+ *
30430
+ * @see clearHighlights
30431
+ * @see HighlightOptions
30432
+ */
30433
+ highlightTrace(trace, options = {}) {
30434
+ if (!this.shadowRoot) {
30435
+ return;
30436
+ }
30437
+ const container = this.shadowRoot.querySelector('.container');
30438
+ if (!container || trace.length === 0) {
30439
+ return;
30440
+ }
30441
+ this.clearHighlights();
30442
+ const color = options.color || '#b71c1c'; // default: a distinct crimson
30443
+ const fadeOthers = options.fadeOthers !== false; // default: true
30444
+ const targetNodes = new Set();
30445
+ const targetEdges = new Set();
30446
+ for (let i = 0; i < trace.length; i++) {
30447
+ targetNodes.add(trace[i]);
30448
+ if (i < trace.length - 1) {
30449
+ targetEdges.add(`${trace[i]}->${trace[i + 1]}`);
30450
+ }
30451
+ }
30452
+ const allNodes = container.querySelectorAll('.node');
30453
+ const allEdges = container.querySelectorAll('.edge');
30454
+ // Graphviz escapes '->' inside <title> as '&#45;&gt;'; DOM textContent
30455
+ // usually decodes it, but normalize defensively (and drop quotes).
30456
+ const unescapeTitle = (title) => title.replace(/&#45;/g, '-').replace(/&gt;/g, '>').replace(/"/g, '');
30457
+ allNodes.forEach(node => {
30458
+ const titleEl = node.querySelector('title');
30459
+ const title = titleEl ? unescapeTitle(titleEl.textContent || '') : '';
30460
+ if (targetNodes.has(title)) {
30461
+ node.querySelectorAll('polygon, ellipse, path').forEach(shape => {
30462
+ shape.style.stroke = color;
30463
+ shape.style.strokeWidth = '2px';
30464
+ });
30465
+ node.querySelectorAll('text').forEach(text => {
30466
+ text.style.fill = color;
30467
+ });
30468
+ }
30469
+ else if (fadeOthers) {
30470
+ node.style.opacity = '0.2';
30471
+ }
30472
+ });
30473
+ allEdges.forEach(edge => {
30474
+ const titleEl = edge.querySelector('title');
30475
+ const title = titleEl ? unescapeTitle(titleEl.textContent || '') : '';
30476
+ if (targetEdges.has(title)) {
30477
+ // Recolour the edge line and its arrowhead. The edge's action label
30478
+ // is intentionally left alone: reorder_svg_layers() hoists every edge
30479
+ // <text> out of its group onto a top paint layer (so labels can't be
30480
+ // overdrawn), where it carries no <title> to correlate back to this
30481
+ // edge — so a state-name trace cannot reliably re-style it.
30482
+ edge.querySelectorAll('path, polygon').forEach(shape => {
30483
+ shape.style.stroke = color;
30484
+ if (shape.tagName.toLowerCase() === 'polygon') {
30485
+ shape.style.fill = color; // arrowheads
30486
+ }
30487
+ });
30488
+ }
30489
+ else if (fadeOthers) {
30490
+ edge.style.opacity = '0.2';
30491
+ }
30492
+ });
30493
+ }
30375
30494
  }
30376
30495
  FslViz.styles = i$4 `
30377
30496
  :host {
@@ -30392,6 +30511,21 @@ FslViz.styles = i$4 `
30392
30511
  width: 90%;
30393
30512
  height: 90%;
30394
30513
  }
30514
+
30515
+ /* Smoothly animate the inline style overrides applied by highlightTrace()
30516
+ so a trace fades in/out rather than snapping. */
30517
+ .container svg g.node path,
30518
+ .container svg g.node polygon,
30519
+ .container svg g.node ellipse,
30520
+ .container svg g.edge path,
30521
+ .container svg g.edge polygon {
30522
+ transition: fill 0.3s ease, stroke 0.3s ease, opacity 0.3s ease;
30523
+ }
30524
+
30525
+ .container svg g.node text,
30526
+ .container svg g.edge text {
30527
+ transition: fill 0.3s ease, opacity 0.3s ease;
30528
+ }
30395
30529
  `;
30396
30530
  __decorate([
30397
30531
  n({ type: String })
@@ -108,7 +108,7 @@ function parseFslArgs(argv, spec) {
108
108
  return { positional, flags, helpText };
109
109
  }
110
110
 
111
- const getVersion = () => "5.155.1";
111
+ const getVersion = () => "5.156.0";
112
112
  const SPEC = {
113
113
  flags: {
114
114
  help: { short: "h", boolean: true },