@typeonce/effect-machine-devtools 0.23.0 → 0.24.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.
Files changed (50) hide show
  1. package/README.md +10 -4
  2. package/dist/DevServer.d.ts +2 -1
  3. package/dist/DevServer.d.ts.map +1 -1
  4. package/dist/DevServer.js.map +1 -1
  5. package/dist/DevToolsProtocol.d.ts +731 -17
  6. package/dist/DevToolsProtocol.d.ts.map +1 -1
  7. package/dist/DevToolsProtocol.js +191 -1
  8. package/dist/DevToolsProtocol.js.map +1 -1
  9. package/dist/MachineDocument.d.ts +78 -2
  10. package/dist/MachineDocument.d.ts.map +1 -1
  11. package/dist/MachineDocument.js +33 -1
  12. package/dist/MachineDocument.js.map +1 -1
  13. package/dist/MachineRegistry.d.ts +36 -6
  14. package/dist/MachineRegistry.d.ts.map +1 -1
  15. package/dist/ProjectInspector.d.ts +2 -0
  16. package/dist/ProjectInspector.d.ts.map +1 -1
  17. package/dist/ProjectInspector.js.map +1 -1
  18. package/dist/client/assets/index-B49Tjawc.js +14 -0
  19. package/dist/client/assets/index-BKH0cOG2.css +1 -0
  20. package/dist/client/index.html +2 -2
  21. package/dist/internal/devServer.d.ts +2 -1
  22. package/dist/internal/devServer.d.ts.map +1 -1
  23. package/dist/internal/devServer.js +68 -7
  24. package/dist/internal/devServer.js.map +1 -1
  25. package/dist/internal/evaluationWorker.d.ts.map +1 -1
  26. package/dist/internal/evaluationWorker.js +279 -4
  27. package/dist/internal/evaluationWorker.js.map +1 -1
  28. package/dist/internal/machineDocument.d.ts.map +1 -1
  29. package/dist/internal/machineDocument.js +63 -2
  30. package/dist/internal/machineDocument.js.map +1 -1
  31. package/dist/internal/projectInspector.d.ts.map +1 -1
  32. package/dist/internal/projectInspector.js +26 -10
  33. package/dist/internal/projectInspector.js.map +1 -1
  34. package/package.json +2 -2
  35. package/src/DevServer.ts +6 -2
  36. package/src/DevToolsProtocol.ts +286 -1
  37. package/src/MachineDocument.ts +54 -1
  38. package/src/ProjectInspector.ts +5 -0
  39. package/src/internal/browser/input-form.ts +669 -0
  40. package/src/internal/browser/planner-example.ts +143 -0
  41. package/src/internal/browser/simulation-client.ts +20 -0
  42. package/src/internal/browser/styles.css +323 -3
  43. package/src/internal/browser/visualizer-app.ts +395 -34
  44. package/src/internal/browser/visualizer.ts +1 -1
  45. package/src/internal/devServer.ts +91 -8
  46. package/src/internal/evaluationWorker.ts +390 -8
  47. package/src/internal/machineDocument.ts +75 -2
  48. package/src/internal/projectInspector.ts +58 -15
  49. package/dist/client/assets/index-BGOhE3Ng.css +0 -1
  50. package/dist/client/assets/index-DiqGhsGR.js +0 -14
@@ -0,0 +1,143 @@
1
+ import { Machine } from "@typeonce/effect-machine"
2
+ import { Schema } from "effect"
3
+
4
+ class Idle extends Schema.TaggedClass<Idle>("PlannerIdle")("Idle", { owner: Schema.String }) {}
5
+ class Working extends Schema.TaggedClass<Working>("PlannerWorking")("Working", {
6
+ owner: Schema.String,
7
+ job: Schema.String
8
+ }) {}
9
+ class Finished extends Schema.TaggedClass<Finished>("PlannerFinished")("Finished", { job: Schema.String }) {}
10
+
11
+ const Owner = Schema.NonEmptyString.annotate({
12
+ title: "Owner",
13
+ description: "A non-empty name carried into the initial Idle state."
14
+ })
15
+ const Attempts = Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 5 })).annotate({
16
+ title: "Attempts",
17
+ description: "An integer between one and five."
18
+ })
19
+ const Labels = Schema.Array(Schema.NonEmptyString).check(Schema.isLengthBetween(1, 3)).annotate({
20
+ title: "Labels",
21
+ description: "One to three non-empty labels."
22
+ })
23
+ const Route = Schema.Union([
24
+ Schema.Literal("direct").annotate({ title: "Direct" }),
25
+ Schema.Struct({
26
+ queue: Schema.NonEmptyString.annotate({
27
+ title: "Queue",
28
+ description: "The queue used by the queued route."
29
+ })
30
+ }).annotate({ title: "Queued" })
31
+ ]).annotate({
32
+ title: "Route",
33
+ description: "Choose a literal direct route or provide a queue."
34
+ })
35
+
36
+ class Begin extends Schema.TaggedClass<Begin>("PlannerBegin")("Begin", {
37
+ job: Schema.NonEmptyString.annotate({
38
+ title: "Job",
39
+ description: "A non-empty job name used as the final output."
40
+ }),
41
+ priority: Schema.Literals(["normal", "urgent"]).annotate({
42
+ title: "Priority",
43
+ description: "Urgent jobs raise AutoFinish during the same macrostep."
44
+ }),
45
+ estimate: Schema.Finite.check(Schema.isBetween({ minimum: 0, maximum: 100 })).annotate({
46
+ title: "Estimate",
47
+ description: "A numeric estimate between zero and one hundred."
48
+ }),
49
+ approved: Schema.Boolean.annotate({
50
+ title: "Approved",
51
+ description: "A boolean checkbox included in the event payload."
52
+ }),
53
+ notes: Schema.optionalKey(
54
+ Schema.String.check(Schema.isMaxLength(80)).annotate({
55
+ title: "Notes",
56
+ description: "Optional text limited to eighty characters."
57
+ })
58
+ ),
59
+ labels: Schema.optionalKey(Labels),
60
+ route: Schema.optionalKey(Route)
61
+ }) {}
62
+ class Cancel extends Schema.TaggedClass<Cancel>("PlannerCancel")("Cancel", { reason: Schema.String }) {}
63
+ class AutoFinish extends Schema.TaggedClass<AutoFinish>("PlannerAutoFinish")("AutoFinish", {}) {}
64
+ class Planned extends Schema.TaggedClass<Planned>("PlannerPlanned")("Planned", { job: Schema.String }) {}
65
+
66
+ const Events = Machine.events(Begin, Cancel)
67
+ const InternalEvents = Machine.internalEvents(AutoFinish)
68
+ const Emissions = Machine.emittedEvents(Planned)
69
+ const States = Machine.states({
70
+ Idle,
71
+ Working,
72
+ Finished: { schema: Finished, type: "final", output: Schema.String }
73
+ })
74
+
75
+ export const plannerMachine = Machine.make({
76
+ id: "planner-example",
77
+ states: States.states,
78
+ events: Events,
79
+ internalEvents: InternalEvents,
80
+ emittedEvents: Emissions,
81
+ input: Schema.Struct({
82
+ owner: Owner,
83
+ attempts: Attempts,
84
+ notifications: Schema.Boolean.annotate({
85
+ title: "Notifications",
86
+ description: "A required boolean with false as a valid value."
87
+ }),
88
+ mode: Schema.Literals(["guided", "automatic"]).annotate({
89
+ title: "Mode",
90
+ description: "A fixed set of startup modes."
91
+ }),
92
+ note: Schema.optionalKey(
93
+ Schema.String.check(Schema.isMaxLength(40)).annotate({
94
+ title: "Note",
95
+ description: "Optional startup text limited to forty characters."
96
+ })
97
+ ),
98
+ labels: Schema.optionalKey(Labels),
99
+ preferences: Schema.optionalKey(
100
+ Schema.Struct({
101
+ retries: Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 3 })).annotate({
102
+ title: "Retries"
103
+ }),
104
+ dryRun: Schema.Boolean.annotate({ title: "Dry run" })
105
+ }).annotate({
106
+ title: "Preferences",
107
+ description: "An optional nested object."
108
+ })
109
+ )
110
+ }),
111
+ initial: (to) => to.Idle().resolve(({ input, target }) => target.decoded(new Idle({ owner: input.owner })))
112
+ }).handle({
113
+ Idle: {
114
+ on: {
115
+ Begin: (to) =>
116
+ to.branches({
117
+ urgent: { title: "Finish immediately", target: to.full.Working() },
118
+ normal: { title: "Wait in working", target: to.full.Working() }
119
+ }).resolve(({ event, self, select, state }, enqueue) => {
120
+ enqueue.emit(new Planned({ job: event.job }))
121
+ enqueue.sendTo(self, Events.Cancel({ reason: "planner command example" }))
122
+ if (event.priority === "urgent") enqueue.raise(InternalEvents.AutoFinish())
123
+ const working = new Working({ owner: state.owner, job: event.job })
124
+ return event.priority === "urgent"
125
+ ? select.urgent.decoded(working)
126
+ : select.normal.decoded(working)
127
+ })
128
+ }
129
+ },
130
+ Working: {
131
+ on: {
132
+ AutoFinish: (to) =>
133
+ to.full.Finished().resolve(({ state, target }) => target.decoded(new Finished({ job: state.job }))),
134
+ Cancel: (to) =>
135
+ to.full.Idle().resolve(({ event, state, target }) =>
136
+ target.decoded(new Idle({ owner: `${state.owner} · ${event.reason}` }))
137
+ )
138
+ }
139
+ },
140
+ Finished: {
141
+ output: ({ state }) => state.job
142
+ }
143
+ })
@@ -0,0 +1,20 @@
1
+ import * as Schema from "effect/Schema"
2
+ import * as DevToolsProtocol from "../../DevToolsProtocol.js"
3
+
4
+ export const requestSimulation = async (
5
+ request: DevToolsProtocol.SimulationRequest
6
+ ): Promise<DevToolsProtocol.SimulationResult> => {
7
+ const response = await fetch("/api/simulations", {
8
+ method: "POST",
9
+ headers: { "content-type": "application/json" },
10
+ body: JSON.stringify(request)
11
+ })
12
+ const body: unknown = await response.json()
13
+ if (!response.ok) {
14
+ const message = typeof body === "object" && body !== null && "message" in body
15
+ ? String(body.message)
16
+ : `Simulation request failed with status ${response.status}`
17
+ throw new Error(message)
18
+ }
19
+ return Schema.decodeUnknownSync(DevToolsProtocol.SimulationResult)(body)
20
+ }
@@ -297,11 +297,11 @@ button {
297
297
  font: 10px/1.5 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
298
298
  }
299
299
 
300
- .simulation-feedback[data-status="indeterminate"] {
300
+ .simulation-feedback[data-status="pending"] {
301
301
  color: #d9ae7e;
302
302
  }
303
303
 
304
- .simulation-feedback[data-status="blocked"] {
304
+ .simulation-feedback[data-status="error"] {
305
305
  color: #d58d89;
306
306
  }
307
307
 
@@ -328,6 +328,11 @@ button {
328
328
  outline: none;
329
329
  }
330
330
 
331
+ .event-button:disabled {
332
+ color: #666d76;
333
+ cursor: wait;
334
+ }
335
+
331
336
  .topology-node {
332
337
  background: transparent;
333
338
  }
@@ -382,7 +387,7 @@ button {
382
387
  }
383
388
 
384
389
  .topology-node.is-related-target > .state-row {
385
- background: rgb(75 125 255 / 9%);
390
+ background: rgb(75 125 255 / 15%);
386
391
  }
387
392
 
388
393
  .topology-node.is-related-source > .state-row {
@@ -746,6 +751,321 @@ button {
746
751
  padding: 0 12px;
747
752
  }
748
753
 
754
+ .simulation-composer {
755
+ display: grid;
756
+ gap: 10px;
757
+ }
758
+
759
+ .simulation-composer .section-heading {
760
+ margin-bottom: 0;
761
+ }
762
+
763
+ .schema-form,
764
+ .input-object,
765
+ .input-array,
766
+ .input-union,
767
+ .input-field,
768
+ .input-array-items {
769
+ display: grid;
770
+ gap: 10px;
771
+ }
772
+
773
+ .schema-form {
774
+ max-width: 520px;
775
+ }
776
+
777
+ .input-object,
778
+ .input-array,
779
+ .input-union {
780
+ min-width: 0;
781
+ margin: 0;
782
+ padding: 0;
783
+ border: 0;
784
+ }
785
+
786
+ .input-object > legend,
787
+ .input-array > legend,
788
+ .input-union > legend {
789
+ margin-bottom: 8px;
790
+ padding: 0;
791
+ color: #d5d8dd;
792
+ font: 600 11px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
793
+ }
794
+
795
+ .input-object .input-object,
796
+ .input-object .input-array,
797
+ .input-object .input-union {
798
+ padding: 11px;
799
+ border: 1px solid var(--line-soft);
800
+ background: #101216;
801
+ }
802
+
803
+ .input-field {
804
+ gap: 6px;
805
+ }
806
+
807
+ .input-field + .input-field {
808
+ padding-top: 10px;
809
+ border-top: 1px solid var(--line-soft);
810
+ }
811
+
812
+ .input-field-heading {
813
+ display: flex;
814
+ align-items: center;
815
+ justify-content: space-between;
816
+ gap: 12px;
817
+ }
818
+
819
+ .input-field-identity,
820
+ .input-constraints {
821
+ display: flex;
822
+ min-width: 0;
823
+ flex-wrap: wrap;
824
+ align-items: center;
825
+ gap: 6px;
826
+ }
827
+
828
+ .input-label,
829
+ .input-required,
830
+ .input-optional,
831
+ .boolean-control {
832
+ font-size: 11px;
833
+ }
834
+
835
+ .input-label {
836
+ color: #cfd3d9;
837
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
838
+ }
839
+
840
+ .input-field-type,
841
+ .input-constraints span {
842
+ color: #8294ad;
843
+ font: 10px/1.3 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
844
+ }
845
+
846
+ .input-field-type {
847
+ padding: 2px 5px;
848
+ border-radius: 3px;
849
+ background: rgb(75 125 255 / 12%);
850
+ }
851
+
852
+ .input-constraints {
853
+ margin-top: -1px;
854
+ }
855
+
856
+ .input-constraints span + span::before {
857
+ margin-right: 6px;
858
+ color: #4f5762;
859
+ content: "·";
860
+ }
861
+
862
+ .input-required {
863
+ color: #737b86;
864
+ }
865
+
866
+ .input-optional,
867
+ .boolean-control {
868
+ display: inline-flex;
869
+ align-items: center;
870
+ gap: 6px;
871
+ color: #9199a4;
872
+ }
873
+
874
+ .input-control {
875
+ width: 100%;
876
+ min-height: 34px;
877
+ padding: 7px 9px;
878
+ border: 1px solid var(--line);
879
+ border-radius: 3px;
880
+ color: #dfe7f5;
881
+ background: #0b0d10;
882
+ font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
883
+ outline: none;
884
+ }
885
+
886
+ .input-control:focus {
887
+ border-color: #557db8;
888
+ }
889
+
890
+ .input-description,
891
+ .input-unsupported,
892
+ .input-errors {
893
+ margin: 0;
894
+ font-size: 11px;
895
+ line-height: 1.55;
896
+ }
897
+
898
+ .input-description {
899
+ color: #737b86;
900
+ }
901
+
902
+ .input-unsupported {
903
+ color: #d7a5a2;
904
+ }
905
+
906
+ .input-errors {
907
+ color: #efaaa6;
908
+ }
909
+
910
+ .input-errors-form {
911
+ padding: 8px 10px;
912
+ border: 1px solid rgb(224 89 84 / 30%);
913
+ background: rgb(224 89 84 / 8%);
914
+ }
915
+
916
+ .literal-control {
917
+ color: #aeb6c1;
918
+ font: 11px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
919
+ }
920
+
921
+ .is-disabled {
922
+ opacity: 0.45;
923
+ }
924
+
925
+ .input-array-item {
926
+ display: grid;
927
+ grid-template-columns: minmax(0, 1fr) auto;
928
+ align-items: start;
929
+ gap: 7px;
930
+ }
931
+
932
+ .input-array-add,
933
+ .input-array-remove {
934
+ padding: 6px 8px;
935
+ border: 0;
936
+ border-radius: 3px;
937
+ color: #9ebce9;
938
+ background: #1b2431;
939
+ font-size: 10px;
940
+ cursor: pointer;
941
+ }
942
+
943
+ .input-array-add {
944
+ justify-self: start;
945
+ }
946
+
947
+ .simulation-action {
948
+ justify-self: start;
949
+ padding: 7px 10px;
950
+ border: 0;
951
+ border-radius: 3px;
952
+ color: #e4edff;
953
+ background: #315d9f;
954
+ font-size: 11px;
955
+ cursor: pointer;
956
+ }
957
+
958
+ .simulation-action:hover,
959
+ .simulation-action:focus-visible {
960
+ background: #3b6db8;
961
+ outline: none;
962
+ }
963
+
964
+ .simulation-action:disabled {
965
+ color: #7e8796;
966
+ background: #252a32;
967
+ cursor: wait;
968
+ }
969
+
970
+ .simulation-note,
971
+ .editor-error {
972
+ margin: 0;
973
+ font-size: 11px;
974
+ line-height: 1.55;
975
+ }
976
+
977
+ .simulation-note {
978
+ color: #777f8a;
979
+ }
980
+
981
+ .editor-error,
982
+ .trace-error {
983
+ color: #eca6a2;
984
+ }
985
+
986
+ .trace-header .json-value {
987
+ margin-top: 18px;
988
+ }
989
+
990
+ .json-value,
991
+ .trace-error {
992
+ max-width: 100%;
993
+ margin: 8px 0 0;
994
+ padding: 10px 11px;
995
+ border: 1px solid var(--line-soft);
996
+ color: #bac8dc;
997
+ background: #0d0f12;
998
+ font: 10px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
999
+ overflow: auto;
1000
+ white-space: pre-wrap;
1001
+ overflow-wrap: anywhere;
1002
+ }
1003
+
1004
+ .trace-topology {
1005
+ display: grid;
1006
+ gap: 10px;
1007
+ }
1008
+
1009
+ .trace-topology .section-heading {
1010
+ margin-bottom: 1px;
1011
+ }
1012
+
1013
+ .trace-path-group,
1014
+ .trace-values,
1015
+ .trace-transitions {
1016
+ display: grid;
1017
+ gap: 7px;
1018
+ }
1019
+
1020
+ .trace-path-group {
1021
+ grid-template-columns: 78px minmax(0, 1fr);
1022
+ align-items: start;
1023
+ }
1024
+
1025
+ .trace-paths {
1026
+ display: flex;
1027
+ min-width: 0;
1028
+ flex-wrap: wrap;
1029
+ gap: 4px 10px;
1030
+ }
1031
+
1032
+ .trace-label {
1033
+ color: #747c87;
1034
+ font-size: 10px;
1035
+ line-height: 1.5;
1036
+ text-transform: uppercase;
1037
+ }
1038
+
1039
+ .trace-card {
1040
+ padding-bottom: 12px;
1041
+ }
1042
+
1043
+ .trace-card > .trace-path-group,
1044
+ .trace-card > .trace-values,
1045
+ .trace-card > .trace-transitions {
1046
+ margin: 0 12px 10px;
1047
+ }
1048
+
1049
+ .trace-transition {
1050
+ display: flex;
1051
+ min-width: 0;
1052
+ flex-wrap: wrap;
1053
+ align-items: center;
1054
+ gap: 6px;
1055
+ }
1056
+
1057
+ .trace-transition + .trace-path-group {
1058
+ margin: 0 0 4px;
1059
+ }
1060
+
1061
+ .trace-results {
1062
+ padding-bottom: 24px;
1063
+ }
1064
+
1065
+ .trace-results > .trace-values {
1066
+ margin-top: 12px;
1067
+ }
1068
+
749
1069
  @media (max-width: 900px) {
750
1070
  .devtools-shell {
751
1071
  grid-template-columns: 1fr;