@typeonce/effect-machine-devtools 0.25.0 → 0.26.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.
@@ -168,7 +168,7 @@ const stateContent = (layout: LaidOutChartNode, stateStatus: HTMLElement): Docum
168
168
  const activities = element("div", "chart-state-section")
169
169
  activities.append(element("div", "chart-section-label", "invokes"))
170
170
  layout.node.activities.slice(0, maxVisibleActivities).forEach((activity) => {
171
- const row = element("div", "chart-activity-row")
171
+ const row = element("div", `chart-activity-row chart-activity-${activity.kind}`)
172
172
  row.append(
173
173
  element("span", "chart-activity-kind", activity.kind),
174
174
  element("span", "chart-activity-name", activity.label)
@@ -184,10 +184,83 @@ const stateContent = (layout: LaidOutChartNode, stateStatus: HTMLElement): Docum
184
184
  return fragment
185
185
  }
186
186
 
187
- const pathData = (points: ReadonlyArray<ChartPoint>): string => {
187
+ const pointAlong = (start: ChartPoint, end: ChartPoint, distance: number): ChartPoint => {
188
+ const length = Math.abs(end.x - start.x) + Math.abs(end.y - start.y)
189
+ if (length === 0) return start
190
+ const ratio = distance / length
191
+ return {
192
+ x: start.x + (end.x - start.x) * ratio,
193
+ y: start.y + (end.y - start.y) * ratio
194
+ }
195
+ }
196
+
197
+ export const chartEdgePathData = (points: ReadonlyArray<ChartPoint>, radius = 9): string => {
188
198
  const first = points[0]
189
199
  if (first === undefined) return ""
190
- return points.slice(1).reduce((path, point) => `${path} L ${point.x} ${point.y}`, `M ${first.x} ${first.y}`)
200
+ if (points.length === 1) return `M ${first.x} ${first.y}`
201
+ let path = `M ${first.x} ${first.y}`
202
+ for (let index = 1; index < points.length - 1; index++) {
203
+ const previous = points[index - 1]!
204
+ const corner = points[index]!
205
+ const next = points[index + 1]!
206
+ const incoming = Math.abs(corner.x - previous.x) + Math.abs(corner.y - previous.y)
207
+ const outgoing = Math.abs(next.x - corner.x) + Math.abs(next.y - corner.y)
208
+ const cornerRadius = Math.min(radius, incoming / 2, outgoing / 2)
209
+ const before = pointAlong(corner, previous, cornerRadius)
210
+ const after = pointAlong(corner, next, cornerRadius)
211
+ path += ` L ${before.x} ${before.y} Q ${corner.x} ${corner.y} ${after.x} ${after.y}`
212
+ }
213
+ const last = points.at(-1)!
214
+ return `${path} L ${last.x} ${last.y}`
215
+ }
216
+
217
+ export interface ChartDirectionCue {
218
+ readonly start: ChartPoint
219
+ readonly end: ChartPoint
220
+ }
221
+
222
+ export const chartDirectionCue = (
223
+ points: ReadonlyArray<ChartPoint>,
224
+ minimumRouteLength = 280
225
+ ): ChartDirectionCue | null => {
226
+ const segments = points.slice(1).map((end, index) => {
227
+ const start = points[index]!
228
+ return {
229
+ start,
230
+ end,
231
+ length: Math.abs(end.x - start.x) + Math.abs(end.y - start.y)
232
+ }
233
+ })
234
+ const total = segments.reduce((sum, segment) => sum + segment.length, 0)
235
+ if (total < minimumRouteLength) return null
236
+ let remaining = total * 0.32
237
+ let selected: { readonly start: ChartPoint; readonly end: ChartPoint; readonly distance: number } | undefined
238
+ for (const segment of segments) {
239
+ if (segment.length >= 24 && remaining <= segment.length) {
240
+ selected = {
241
+ start: segment.start,
242
+ end: segment.end,
243
+ distance: Math.min(segment.length - 12, Math.max(12, remaining))
244
+ }
245
+ break
246
+ }
247
+ remaining -= segment.length
248
+ }
249
+ if (selected === undefined) {
250
+ const segment = [...segments].sort((left, right) => right.length - left.length)[0]
251
+ if (segment === undefined || segment.length < 24) return null
252
+ selected = { start: segment.start, end: segment.end, distance: segment.length / 2 }
253
+ }
254
+ const center = pointAlong(selected.start, selected.end, selected.distance)
255
+ const deltaX = selected.end.x - selected.start.x
256
+ const deltaY = selected.end.y - selected.start.y
257
+ const horizontal = Math.abs(deltaX) >= Math.abs(deltaY)
258
+ const directionX = horizontal ? Math.sign(deltaX) : 0
259
+ const directionY = horizontal ? 0 : Math.sign(deltaY)
260
+ return {
261
+ start: { x: center.x - directionX * 5, y: center.y - directionY * 5 },
262
+ end: { x: center.x + directionX * 5, y: center.y + directionY * 5 }
263
+ }
191
264
  }
192
265
 
193
266
  const setStateClass = (
@@ -219,16 +292,29 @@ const render = (
219
292
  const definitions = svgElement("defs")
220
293
  const marker = svgElement("marker")
221
294
  marker.id = "chart-arrow"
222
- marker.setAttribute("viewBox", "0 0 10 10")
223
- marker.setAttribute("refX", "9")
224
- marker.setAttribute("refY", "5")
225
- marker.setAttribute("markerWidth", "7")
226
- marker.setAttribute("markerHeight", "7")
295
+ marker.setAttribute("viewBox", "0 0 12 12")
296
+ marker.setAttribute("refX", "11")
297
+ marker.setAttribute("refY", "6")
298
+ marker.setAttribute("markerWidth", "12")
299
+ marker.setAttribute("markerHeight", "12")
300
+ marker.setAttribute("markerUnits", "userSpaceOnUse")
227
301
  marker.setAttribute("orient", "auto-start-reverse")
228
302
  const arrow = svgElement("path")
229
- arrow.setAttribute("d", "M 0 0 L 10 5 L 0 10 z")
303
+ arrow.setAttribute("d", "M 1 1 L 11 6 L 1 11 z")
230
304
  marker.append(arrow)
231
- definitions.append(marker)
305
+ const directionMarker = svgElement("marker")
306
+ directionMarker.id = "chart-direction"
307
+ directionMarker.setAttribute("viewBox", "0 0 8 8")
308
+ directionMarker.setAttribute("refX", "7")
309
+ directionMarker.setAttribute("refY", "4")
310
+ directionMarker.setAttribute("markerWidth", "8")
311
+ directionMarker.setAttribute("markerHeight", "8")
312
+ directionMarker.setAttribute("markerUnits", "userSpaceOnUse")
313
+ directionMarker.setAttribute("orient", "auto")
314
+ const directionArrow = svgElement("path")
315
+ directionArrow.setAttribute("d", "M 1 1 L 7 4 L 1 7 z")
316
+ directionMarker.append(directionArrow)
317
+ definitions.append(marker, directionMarker)
232
318
  svg.append(definitions)
233
319
  const nodesLayer = element("div", "chart-nodes")
234
320
  const labelsLayer = element("div", "chart-labels")
@@ -274,6 +360,15 @@ const render = (
274
360
  edgeElements.set(id, registered)
275
361
  }
276
362
 
363
+ for (const regionLayout of layout.regions) {
364
+ const region = element("div", "chart-unconnected-region")
365
+ region.title = "No statically known transition reaches these states. Runtime-resolved targets may still reach them."
366
+ position(region, regionLayout)
367
+ const label = element("div", "chart-unconnected-region-label", "No static path from initial")
368
+ region.append(label)
369
+ regions.append(region)
370
+ }
371
+
277
372
  for (const laidOut of layout.nodes) {
278
373
  if (laidOut.node.children.length > 0) {
279
374
  const region = element("div", `chart-compound chart-compound-${laidOut.node.type}`)
@@ -337,16 +432,32 @@ const render = (
337
432
  const group = svgElement(
338
433
  "g",
339
434
  `chart-edge-group chart-edge-${laidOut.kind}${
340
- laidOut.kind === "transition" ? ` chart-transition-${laidOut.edge.kind}` : ""
435
+ laidOut.kind === "transition"
436
+ ? ` chart-transition-${laidOut.edge.kind} chart-edge-trigger-${laidOut.edge.trigger.type}${
437
+ laidOut.edge.activityKind === null ? "" : ` chart-edge-activity-${laidOut.edge.activityKind}`
438
+ }`
439
+ : ""
341
440
  }`
342
441
  )
442
+ const casing = svgElement("path", "chart-edge-casing")
343
443
  const visible = svgElement("path", "chart-edge-line")
344
- const route = pathData(laidOut.points)
444
+ const route = chartEdgePathData(laidOut.points)
445
+ casing.setAttribute("d", route)
345
446
  visible.setAttribute("d", route)
346
447
  visible.setAttribute("marker-end", "url(#chart-arrow)")
347
448
  const hit = svgElement("path", "chart-edge-hit")
348
449
  hit.setAttribute("d", route)
349
- group.append(visible, hit)
450
+ group.append(casing, visible)
451
+ if (laidOut.kind === "transition") {
452
+ const cue = chartDirectionCue(laidOut.points)
453
+ if (cue !== null) {
454
+ const direction = svgElement("path", "chart-edge-direction")
455
+ direction.setAttribute("d", `M ${cue.start.x} ${cue.start.y} L ${cue.end.x} ${cue.end.y}`)
456
+ direction.setAttribute("marker-end", "url(#chart-direction)")
457
+ group.append(direction)
458
+ }
459
+ }
460
+ group.append(hit)
350
461
  svg.append(group)
351
462
  if (laidOut.kind === "initial") continue
352
463
 
@@ -354,7 +465,9 @@ const render = (
354
465
  registerEdgeElement(laidOut.edge.id, group)
355
466
  const label = element(
356
467
  "button",
357
- `chart-edge-label chart-edge-label-${laidOut.edge.trigger.type}`,
468
+ `chart-edge-label chart-edge-label-${laidOut.edge.trigger.type}${
469
+ laidOut.edge.activityKind === null ? "" : ` chart-edge-activity-${laidOut.edge.activityKind}`
470
+ }`,
358
471
  laidOut.edge.label
359
472
  )
360
473
  label.type = "button"
@@ -308,6 +308,31 @@ button {
308
308
  cursor: default;
309
309
  }
310
310
 
311
+ .toolbar-button[aria-pressed="true"] {
312
+ color: #dce8ff;
313
+ background: rgb(65 103 165 / 28%);
314
+ }
315
+
316
+ .analysis-button {
317
+ display: inline-flex;
318
+ align-items: center;
319
+ gap: 6px;
320
+ }
321
+
322
+ .analysis-button[hidden] {
323
+ display: none;
324
+ }
325
+
326
+ .analysis-button-count {
327
+ min-width: 17px;
328
+ padding: 2px 5px;
329
+ border-radius: 8px;
330
+ color: #9da6b3;
331
+ background: #20242a;
332
+ font: 10px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
333
+ text-align: center;
334
+ }
335
+
311
336
  .topology-chart {
312
337
  display: flex;
313
338
  flex: 1;
@@ -411,6 +436,23 @@ button {
411
436
  background: rgb(20 24 30 / 74%);
412
437
  }
413
438
 
439
+ .chart-unconnected-region {
440
+ position: absolute;
441
+ border: 1px dashed #353b44;
442
+ border-radius: 5px;
443
+ background: rgb(14 16 19 / 58%);
444
+ }
445
+
446
+ .chart-unconnected-region-label {
447
+ position: absolute;
448
+ top: 17px;
449
+ left: 24px;
450
+ color: #727984;
451
+ font: 9px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
452
+ letter-spacing: 0.05em;
453
+ text-transform: uppercase;
454
+ }
455
+
414
456
  .chart-state {
415
457
  position: absolute;
416
458
  display: block;
@@ -558,11 +600,51 @@ button {
558
600
 
559
601
  .chart-activity-kind {
560
602
  min-width: max-content;
561
- color: #77a990;
603
+ color: var(--chart-activity-color, #77a990);
562
604
  overflow: visible;
563
605
  text-overflow: clip;
564
606
  }
565
607
 
608
+ .chart-activity-process,
609
+ .chart-edge-activity-process,
610
+ .badge-activity-process {
611
+ --chart-activity-color: #a98978;
612
+ --chart-activity-border: #51433c;
613
+ --chart-activity-background: #191513;
614
+ }
615
+
616
+ .chart-activity-effect,
617
+ .chart-edge-activity-effect,
618
+ .badge-activity-effect {
619
+ --chart-activity-color: #7f9f8c;
620
+ --chart-activity-border: #42564b;
621
+ --chart-activity-background: #131815;
622
+ }
623
+
624
+ .chart-activity-timer,
625
+ .chart-edge-activity-timer,
626
+ .badge-activity-timer {
627
+ --chart-activity-color: #aaa07c;
628
+ --chart-activity-border: #57503c;
629
+ --chart-activity-background: #191812;
630
+ }
631
+
632
+ .chart-activity-stream,
633
+ .chart-edge-activity-stream,
634
+ .badge-activity-stream {
635
+ --chart-activity-color: #7898a2;
636
+ --chart-activity-border: #405259;
637
+ --chart-activity-background: #121719;
638
+ }
639
+
640
+ .chart-activity-machine,
641
+ .chart-edge-activity-machine,
642
+ .badge-activity-machine {
643
+ --chart-activity-color: #958aa8;
644
+ --chart-activity-border: #4d4658;
645
+ --chart-activity-background: #17151a;
646
+ }
647
+
566
648
  .chart-more {
567
649
  min-height: 18px;
568
650
  padding-top: 3px;
@@ -591,17 +673,30 @@ button {
591
673
  user-select: none;
592
674
  }
593
675
 
676
+ .chart-edge-casing,
594
677
  .chart-edge-line,
678
+ .chart-edge-direction,
595
679
  .chart-edge-hit {
596
680
  fill: none;
597
681
  }
598
682
 
599
- .chart-edge-line {
683
+ .chart-edge-casing {
684
+ stroke: #0b0c0e;
685
+ stroke-width: 5.5;
686
+ vector-effect: non-scaling-stroke;
687
+ }
688
+
689
+ .chart-edge-line,
690
+ .chart-edge-direction {
600
691
  stroke: #626b77;
601
692
  stroke-width: 1.4;
602
693
  vector-effect: non-scaling-stroke;
603
694
  }
604
695
 
696
+ .chart-edge-direction {
697
+ pointer-events: none;
698
+ }
699
+
605
700
  .chart-edge-initial .chart-edge-line {
606
701
  stroke: #89929e;
607
702
  }
@@ -613,7 +708,8 @@ button {
613
708
  pointer-events: stroke;
614
709
  }
615
710
 
616
- #chart-arrow path {
711
+ #chart-arrow path,
712
+ #chart-direction path {
617
713
  fill: context-stroke;
618
714
  }
619
715
 
@@ -647,7 +743,8 @@ button {
647
743
  color: #d4b4e9;
648
744
  }
649
745
 
650
- .chart-edge-group.is-hovered .chart-edge-line {
746
+ .chart-edge-group.is-hovered .chart-edge-line,
747
+ .chart-edge-group.is-hovered .chart-edge-direction {
651
748
  stroke: #7c9ed2;
652
749
  stroke-width: 1.8;
653
750
  }
@@ -661,22 +758,56 @@ button {
661
758
  stroke-dasharray: 5 5;
662
759
  }
663
760
 
664
- .chart-edge-group.is-incoming .chart-edge-line {
761
+ .chart-edge-group.chart-edge-trigger-always .chart-edge-line,
762
+ .chart-edge-group.chart-edge-trigger-choice .chart-edge-line,
763
+ .chart-edge-group.chart-edge-trigger-done .chart-edge-line,
764
+ .chart-edge-group.chart-edge-trigger-invoke .chart-edge-line {
765
+ stroke-dasharray: 7 5;
766
+ }
767
+
768
+ .chart-edge-group.chart-edge-activity-process .chart-edge-line,
769
+ .chart-edge-group.chart-edge-activity-effect .chart-edge-line,
770
+ .chart-edge-group.chart-edge-activity-timer .chart-edge-line,
771
+ .chart-edge-group.chart-edge-activity-stream .chart-edge-line,
772
+ .chart-edge-group.chart-edge-activity-machine .chart-edge-line,
773
+ .chart-edge-group.chart-edge-activity-process .chart-edge-direction,
774
+ .chart-edge-group.chart-edge-activity-effect .chart-edge-direction,
775
+ .chart-edge-group.chart-edge-activity-timer .chart-edge-direction,
776
+ .chart-edge-group.chart-edge-activity-stream .chart-edge-direction,
777
+ .chart-edge-group.chart-edge-activity-machine .chart-edge-direction {
778
+ stroke: var(--chart-activity-color);
779
+ }
780
+
781
+ .chart-edge-label.chart-edge-activity-process,
782
+ .chart-edge-label.chart-edge-activity-effect,
783
+ .chart-edge-label.chart-edge-activity-timer,
784
+ .chart-edge-label.chart-edge-activity-stream,
785
+ .chart-edge-label.chart-edge-activity-machine {
786
+ border-color: var(--chart-activity-border);
787
+ color: var(--chart-activity-color);
788
+ background: var(--chart-activity-background);
789
+ }
790
+
791
+ .chart-edge-group.is-incoming .chart-edge-line,
792
+ .chart-edge-group.is-incoming .chart-edge-direction {
665
793
  stroke: #ed6a70;
666
794
  stroke-width: 2;
667
795
  }
668
796
 
669
- .chart-edge-group.is-outgoing .chart-edge-line {
797
+ .chart-edge-group.is-outgoing .chart-edge-line,
798
+ .chart-edge-group.is-outgoing .chart-edge-direction {
670
799
  stroke: #6eb6dc;
671
800
  stroke-width: 2;
672
801
  }
673
802
 
674
- .chart-edge-group.is-selected .chart-edge-line {
803
+ .chart-edge-group.is-selected .chart-edge-line,
804
+ .chart-edge-group.is-selected .chart-edge-direction {
675
805
  stroke: #8ab5ff;
676
806
  stroke-width: 2.5;
677
807
  }
678
808
 
679
- .chart-edge-group.is-walkthrough-available .chart-edge-line {
809
+ .chart-edge-group.is-walkthrough-available .chart-edge-line,
810
+ .chart-edge-group.is-walkthrough-available .chart-edge-direction {
680
811
  stroke: #d5aa57;
681
812
  stroke-width: 2;
682
813
  }
@@ -827,6 +958,15 @@ button {
827
958
  background: rgb(78 181 131 / 14%);
828
959
  }
829
960
 
961
+ .badge-activity-process,
962
+ .badge-activity-effect,
963
+ .badge-activity-timer,
964
+ .badge-activity-stream,
965
+ .badge-activity-machine {
966
+ color: var(--chart-activity-color);
967
+ background: var(--chart-activity-background);
968
+ }
969
+
830
970
  .badge-count {
831
971
  color: #858c96;
832
972
  background: transparent;
@@ -835,7 +975,7 @@ button {
835
975
  .inspector {
836
976
  position: absolute;
837
977
  z-index: 10;
838
- top: 14px;
978
+ top: 60px;
839
979
  right: 14px;
840
980
  bottom: 14px;
841
981
  width: min(460px, calc(100% - 28px));
@@ -973,6 +1113,13 @@ button {
973
1113
  border-bottom: 1px solid var(--line);
974
1114
  }
975
1115
 
1116
+ .analysis-summary {
1117
+ margin: 12px 0 0;
1118
+ color: #7f8690;
1119
+ font-size: 12px;
1120
+ line-height: 1.6;
1121
+ }
1122
+
976
1123
  .state-annotations {
977
1124
  margin-top: 18px;
978
1125
  }
@@ -1071,6 +1218,33 @@ button {
1071
1218
  background: var(--surface-raised);
1072
1219
  }
1073
1220
 
1221
+ .analysis-card {
1222
+ padding-bottom: 12px;
1223
+ }
1224
+
1225
+ .analysis-card .state-link {
1226
+ color: #e2e4e7;
1227
+ font-size: 12px;
1228
+ font-weight: 600;
1229
+ }
1230
+
1231
+ .analysis-message,
1232
+ .analysis-qualification {
1233
+ margin: 0;
1234
+ padding: 0 12px;
1235
+ font-size: 11px;
1236
+ line-height: 1.6;
1237
+ }
1238
+
1239
+ .analysis-message {
1240
+ color: #b9bec6;
1241
+ }
1242
+
1243
+ .analysis-qualification {
1244
+ margin-top: 5px;
1245
+ color: #747c87;
1246
+ }
1247
+
1074
1248
  .card-header {
1075
1249
  display: flex;
1076
1250
  min-height: 42px;
@@ -1546,7 +1720,7 @@ button {
1546
1720
  }
1547
1721
 
1548
1722
  .inspector {
1549
- top: 8px;
1723
+ top: 54px;
1550
1724
  right: 8px;
1551
1725
  bottom: 8px;
1552
1726
  width: min(440px, calc(100% - 16px));
@@ -1572,6 +1746,10 @@ button {
1572
1746
  padding: 48px 14px 18px;
1573
1747
  }
1574
1748
 
1749
+ .inspector {
1750
+ top: 84px;
1751
+ }
1752
+
1575
1753
  .zoom-controls {
1576
1754
  bottom: 8px;
1577
1755
  left: 8px;
@@ -33,6 +33,9 @@ class Paused extends Schema.TaggedClass<Paused>("TransitionPaused")("Paused", {
33
33
  class Published extends Schema.TaggedClass<Published>("TransitionPublished")("Published", {
34
34
  result: Schema.String
35
35
  }) {}
36
+ class Disabled extends Schema.TaggedClass<Disabled>("TransitionDisabled")("Disabled", {
37
+ reason: Schema.String
38
+ }) {}
36
39
 
37
40
  class Create extends Schema.TaggedClass<Create>("TransitionCreate")("Create", {
38
41
  text: Schema.String,
@@ -67,6 +70,9 @@ class BumpWorkspace extends Schema.TaggedClass<BumpWorkspace>("TransitionBumpWor
67
70
  "BumpWorkspace",
68
71
  {}
69
72
  ) {}
73
+ class Archive extends Schema.TaggedClass<Archive>("TransitionArchive")("Archive", {
74
+ reason: Schema.String
75
+ }) {}
70
76
 
71
77
  const TransitionStates = Machine.states({
72
78
  Workspace: {
@@ -91,6 +97,7 @@ const TransitionStates = Machine.states({
91
97
  }
92
98
  },
93
99
  Paused,
100
+ Disabled,
94
101
  Published: { schema: Published, type: "final", output: Schema.String }
95
102
  })
96
103
 
@@ -121,7 +128,8 @@ export const transitionSemanticsMachine = Machine.make({
121
128
  Refresh,
122
129
  Ignore,
123
130
  MaybeHandle,
124
- BumpWorkspace
131
+ BumpWorkspace,
132
+ Archive
125
133
  ),
126
134
  initial: (to) => to.Paused().resolve(({ target }) => target.decoded(new Paused({ reason: "not started" })))
127
135
  }).handle({
@@ -230,6 +238,7 @@ export const transitionSemanticsMachine = Machine.make({
230
238
  )
231
239
  }
232
240
  },
241
+ Disabled: {},
233
242
  Published: {
234
243
  output: ({ state }) => state.result
235
244
  }
@@ -0,0 +1,52 @@
1
+ import type { MachineDocument as VisualizationDocument } from "../../MachineDocument.js"
2
+ import { makeChartLayoutPolicy } from "./chart-layout-policy.js"
3
+ import { makeChartModel } from "./chart-model.js"
4
+
5
+ export interface UnhandledPublicEventWarning {
6
+ readonly _tag: "UnhandledPublicEvent"
7
+ readonly event: string
8
+ }
9
+
10
+ export interface NoStaticPathNote {
11
+ readonly _tag: "NoStaticPathFromInitial"
12
+ readonly path: string
13
+ readonly label: string
14
+ readonly type: "atomic" | "compound" | "parallel" | "final" | "history" | "choice"
15
+ readonly descendantCount: number
16
+ }
17
+
18
+ export interface VisualizerAnalysis {
19
+ readonly warnings: ReadonlyArray<UnhandledPublicEventWarning>
20
+ readonly topologyNotes: ReadonlyArray<NoStaticPathNote>
21
+ }
22
+
23
+ /** Static findings derived without executing machine callbacks. */
24
+ export const analyzeVisualization = (document: VisualizationDocument): VisualizerAnalysis => {
25
+ const handledEvents = new Set(
26
+ document.transitions.flatMap((transition): ReadonlyArray<string> =>
27
+ transition.trigger.type === "event" ? [transition.trigger.event] : []
28
+ )
29
+ )
30
+ const declaredEvents = new Set(document.inputs.events.map(({ event }) => event))
31
+ const warnings = [...declaredEvents].flatMap((event): ReadonlyArray<UnhandledPublicEventWarning> =>
32
+ handledEvents.has(event) ? [] : [{ _tag: "UnhandledPublicEvent", event }]
33
+ )
34
+
35
+ const chart = makeChartModel(document)
36
+ const policy = makeChartLayoutPolicy(chart)
37
+ const topologyNotes = chart.nodes.flatMap((node): ReadonlyArray<NoStaticPathNote> => {
38
+ const hasStaticPath = policy.node(node.path).staticPath
39
+ const parentHasStaticPath = node.parent === null || policy.node(node.parent).staticPath
40
+ if (hasStaticPath || !parentHasStaticPath) return []
41
+ const descendantCount = chart.nodes.filter(({ path }) => path.startsWith(`${node.path}.`)).length
42
+ return [{
43
+ _tag: "NoStaticPathFromInitial",
44
+ path: node.path,
45
+ label: node.label,
46
+ type: node.type,
47
+ descendantCount
48
+ }]
49
+ })
50
+
51
+ return { warnings, topologyNotes }
52
+ }