@typeonce/effect-machine-devtools 0.23.0 → 0.25.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 (80) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +14 -8
  3. package/dist/DevServer.d.ts +2 -1
  4. package/dist/DevServer.d.ts.map +1 -1
  5. package/dist/DevServer.js.map +1 -1
  6. package/dist/DevToolsProtocol.d.ts +167 -17
  7. package/dist/DevToolsProtocol.d.ts.map +1 -1
  8. package/dist/DevToolsProtocol.js +1 -1
  9. package/dist/MachineDocument.d.ts +98 -2
  10. package/dist/MachineDocument.d.ts.map +1 -1
  11. package/dist/MachineDocument.js +35 -1
  12. package/dist/MachineDocument.js.map +1 -1
  13. package/dist/MachineRegistry.d.ts +56 -6
  14. package/dist/MachineRegistry.d.ts.map +1 -1
  15. package/dist/MachineWalkthrough.d.ts +171 -0
  16. package/dist/MachineWalkthrough.d.ts.map +1 -0
  17. package/dist/MachineWalkthrough.js +98 -0
  18. package/dist/MachineWalkthrough.js.map +1 -0
  19. package/dist/client/assets/index-BTOAhezX.js +37 -0
  20. package/dist/client/assets/index-Cg7xzSkz.css +1 -0
  21. package/dist/client/index.html +4 -4
  22. package/dist/index.d.ts +2 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2 -2
  25. package/dist/index.js.map +1 -1
  26. package/dist/internal/devServer.d.ts +2 -1
  27. package/dist/internal/devServer.d.ts.map +1 -1
  28. package/dist/internal/devServer.js +4 -3
  29. package/dist/internal/devServer.js.map +1 -1
  30. package/dist/internal/evaluationWorker.d.ts.map +1 -1
  31. package/dist/internal/evaluationWorker.js +28 -9
  32. package/dist/internal/evaluationWorker.js.map +1 -1
  33. package/dist/internal/machineDocument.d.ts.map +1 -1
  34. package/dist/internal/machineDocument.js +65 -2
  35. package/dist/internal/machineDocument.js.map +1 -1
  36. package/dist/internal/machineWalkthrough.d.ts +26 -0
  37. package/dist/internal/machineWalkthrough.d.ts.map +1 -0
  38. package/dist/internal/machineWalkthrough.js +256 -0
  39. package/dist/internal/machineWalkthrough.js.map +1 -0
  40. package/dist/internal/projectInspector.d.ts.map +1 -1
  41. package/dist/internal/projectInspector.js +15 -9
  42. package/dist/internal/projectInspector.js.map +1 -1
  43. package/index.html +2 -2
  44. package/package.json +4 -3
  45. package/src/DevServer.ts +6 -2
  46. package/src/DevToolsProtocol.ts +1 -1
  47. package/src/MachineDocument.ts +56 -1
  48. package/src/MachineWalkthrough.ts +199 -0
  49. package/src/index.ts +2 -2
  50. package/src/internal/browser/chart-layout.ts +433 -0
  51. package/src/internal/browser/chart-model.ts +234 -0
  52. package/src/internal/browser/chart-renderer.ts +661 -0
  53. package/src/internal/browser/example-machine.ts +15 -6
  54. package/src/internal/browser/input-form.ts +252 -0
  55. package/src/internal/browser/invoke-outcomes-example.ts +231 -0
  56. package/src/internal/browser/parallel-completion-example.ts +199 -0
  57. package/src/internal/browser/planner-example.ts +145 -0
  58. package/src/internal/browser/protocol-events-example.ts +200 -0
  59. package/src/internal/browser/styles.css +959 -185
  60. package/src/internal/browser/transition-semantics-example.ts +236 -0
  61. package/src/internal/browser/visualizer-app.ts +818 -389
  62. package/src/internal/browser/visualizer-model.ts +104 -0
  63. package/src/internal/browser/visualizer.ts +1 -1
  64. package/src/internal/devServer.ts +9 -4
  65. package/src/internal/evaluationWorker.ts +28 -10
  66. package/src/internal/machineDocument.ts +77 -2
  67. package/src/internal/machineWalkthrough.ts +355 -0
  68. package/src/internal/projectInspector.ts +29 -14
  69. package/dist/MachineSimulator.d.ts +0 -169
  70. package/dist/MachineSimulator.d.ts.map +0 -1
  71. package/dist/MachineSimulator.js +0 -98
  72. package/dist/MachineSimulator.js.map +0 -1
  73. package/dist/client/assets/index-BGOhE3Ng.css +0 -1
  74. package/dist/client/assets/index-DiqGhsGR.js +0 -14
  75. package/dist/internal/machineSimulator.d.ts +0 -5
  76. package/dist/internal/machineSimulator.d.ts.map +0 -1
  77. package/dist/internal/machineSimulator.js +0 -156
  78. package/dist/internal/machineSimulator.js.map +0 -1
  79. package/src/MachineSimulator.ts +0 -154
  80. package/src/internal/machineSimulator.ts +0 -199
@@ -1,154 +0,0 @@
1
- /**
2
- * Side-effect-free, best-effort simulation over a machine document.
3
- *
4
- * @since 0.23.0
5
- */
6
- import * as Schema from "effect/Schema"
7
- import * as internal from "./internal/machineSimulator.js"
8
- import type * as MachineDocument from "./MachineDocument.js"
9
-
10
- /**
11
- * Serializable state of a simulation session.
12
- *
13
- * @category schemas
14
- * @since 0.23.0
15
- */
16
- export const Snapshot = Schema.Struct({
17
- step: Schema.Natural,
18
- activePaths: Schema.Array(Schema.String),
19
- candidateEvents: Schema.Array(Schema.String)
20
- })
21
-
22
- /**
23
- * @category models
24
- * @since 0.23.0
25
- */
26
- export type Snapshot = Schema.Schema.Type<typeof Snapshot>
27
-
28
- /**
29
- * Opaque simulation state paired with its source document.
30
- *
31
- * @category models
32
- * @since 0.23.0
33
- */
34
- export interface Session {
35
- readonly document: MachineDocument.MachineDocument
36
- readonly snapshot: Snapshot
37
- }
38
-
39
- /**
40
- * Runtime behavior deliberately skipped by a best-effort step.
41
- *
42
- * @category schemas
43
- * @since 0.23.0
44
- */
45
- export const Note = Schema.Literals([
46
- "runtime-effects-skipped",
47
- "state-updates-skipped",
48
- "reentry-lifecycles-skipped",
49
- "automatic-transitions-skipped"
50
- ])
51
-
52
- /**
53
- * @category models
54
- * @since 0.23.0
55
- */
56
- export type Note = Schema.Schema.Type<typeof Note>
57
-
58
- const ResultFields = {
59
- event: Schema.String,
60
- transitionIds: Schema.Array(Schema.String),
61
- session: Snapshot
62
- }
63
-
64
- /**
65
- * A topologically deterministic step.
66
- *
67
- * @category schemas
68
- * @since 0.23.0
69
- */
70
- export const Applied = Schema.Struct({
71
- ...ResultFields,
72
- _tag: Schema.tag("Applied"),
73
- notes: Schema.Array(Note)
74
- })
75
-
76
- /**
77
- * @category models
78
- * @since 0.23.0
79
- */
80
- export type Applied = Schema.Schema.Type<typeof Applied>
81
-
82
- /**
83
- * An event with no registration in the current active configuration.
84
- *
85
- * @category schemas
86
- * @since 0.23.0
87
- */
88
- export const Blocked = Schema.Struct({
89
- ...ResultFields,
90
- _tag: Schema.tag("Blocked"),
91
- reason: Schema.Literal("event-not-enabled")
92
- })
93
-
94
- /**
95
- * @category models
96
- * @since 0.23.0
97
- */
98
- export type Blocked = Schema.Schema.Type<typeof Blocked>
99
-
100
- /**
101
- * A step whose topology depends on runtime behavior the document cannot safely
102
- * evaluate.
103
- *
104
- * @category schemas
105
- * @since 0.23.0
106
- */
107
- export const Indeterminate = Schema.Struct({
108
- ...ResultFields,
109
- _tag: Schema.tag("Indeterminate"),
110
- reason: Schema.Literals([
111
- "multiple-transitions",
112
- "declinable-transition",
113
- "conditional-branches",
114
- "history-target",
115
- "choice-target",
116
- "missing-target"
117
- ])
118
- })
119
-
120
- /**
121
- * @category models
122
- * @since 0.23.0
123
- */
124
- export type Indeterminate = Schema.Schema.Type<typeof Indeterminate>
125
-
126
- /**
127
- * @category schemas
128
- * @since 0.23.0
129
- */
130
- export const StepResult = Schema.Union([Applied, Blocked, Indeterminate])
131
-
132
- /**
133
- * @category models
134
- * @since 0.23.0
135
- */
136
- export type StepResult = Schema.Schema.Type<typeof StepResult>
137
-
138
- /**
139
- * Starts from the captured snapshot when present, otherwise from the static
140
- * initial topology.
141
- *
142
- * @category constructors
143
- * @since 0.23.0
144
- */
145
- export const start: (document: MachineDocument.MachineDocument) => Session = internal.start
146
-
147
- /**
148
- * Sends an event without running user code. `Applied` means the active topology
149
- * is statically known; its notes list runtime behavior that was skipped.
150
- *
151
- * @category combinators
152
- * @since 0.23.0
153
- */
154
- export const send: (session: Session, event: string) => StepResult = internal.send
@@ -1,199 +0,0 @@
1
- import type * as MachineDocument from "../MachineDocument.js"
2
- import type * as MachineSimulator from "../MachineSimulator.js"
3
-
4
- type State = MachineDocument.State
5
- type Transition = MachineDocument.Transition
6
-
7
- interface Model {
8
- readonly states: ReadonlyMap<string, State>
9
- readonly order: ReadonlyMap<string, number>
10
- }
11
-
12
- const makeModel = (document: MachineDocument.MachineDocument): Model => ({
13
- states: new Map(document.states.map((state) => [state.path, state])),
14
- order: new Map(document.states.map((state) => [state.path, state.order]))
15
- })
16
-
17
- const ordered = (model: Model, paths: Iterable<string>): ReadonlyArray<string> =>
18
- [...new Set(paths)].sort((left, right) => (model.order.get(left) ?? 0) - (model.order.get(right) ?? 0))
19
-
20
- const enter = (model: Model, path: string, active: Set<string>): void => {
21
- const state = model.states.get(path)
22
- if (state === undefined) return
23
- active.add(path)
24
- if (state.type === "parallel") {
25
- state.children.forEach((child) => enter(model, child, active))
26
- } else if (state.type === "compound" && state.initial !== null) {
27
- enter(model, state.initial, active)
28
- }
29
- }
30
-
31
- const ancestors = (model: Model, path: string): ReadonlyArray<string> => {
32
- const result: Array<string> = []
33
- let current = model.states.get(path)
34
- while (current !== undefined) {
35
- result.unshift(current.path)
36
- current = current.parent === null ? undefined : model.states.get(current.parent)
37
- }
38
- return result
39
- }
40
-
41
- const leastCommonAncestor = (model: Model, left: string, right: string): string | null => {
42
- const leftAncestors = ancestors(model, left)
43
- const rightAncestors = ancestors(model, right)
44
- let result: string | null = null
45
- const length = Math.min(leftAncestors.length, rightAncestors.length)
46
- for (let index = 0; index < length; index++) {
47
- if (leftAncestors[index] !== rightAncestors[index]) break
48
- result = leftAncestors[index] ?? null
49
- }
50
- return result
51
- }
52
-
53
- const isDescendant = (model: Model, path: string, ancestor: string): boolean =>
54
- path !== ancestor && ancestors(model, path).includes(ancestor)
55
-
56
- const candidateEvents = (
57
- document: MachineDocument.MachineDocument,
58
- activePaths: ReadonlyArray<string>
59
- ): ReadonlyArray<string> => {
60
- const active = new Set(activePaths)
61
- return [
62
- ...new Set(
63
- document.transitions.flatMap((transition): ReadonlyArray<string> =>
64
- transition.trigger.type === "event" && active.has(transition.source) ? [transition.trigger.event] : []
65
- )
66
- )
67
- ].sort()
68
- }
69
-
70
- const snapshot = (
71
- document: MachineDocument.MachineDocument,
72
- model: Model,
73
- step: number,
74
- activePaths: Iterable<string>
75
- ): MachineSimulator.Snapshot => {
76
- const active = ordered(model, activePaths)
77
- return {
78
- step,
79
- activePaths: active,
80
- candidateEvents: candidateEvents(document, active)
81
- }
82
- }
83
-
84
- export const start = (document: MachineDocument.MachineDocument): MachineSimulator.Session => {
85
- const model = makeModel(document)
86
- const active = new Set<string>()
87
- if (document.snapshot === null) {
88
- const targetAncestors = ancestors(model, document.initial.target)
89
- targetAncestors.forEach((path) => active.add(path))
90
- enter(model, document.initial.target, active)
91
- } else {
92
- document.snapshot.activePaths.forEach((path) => active.add(path))
93
- }
94
- return { document, snapshot: snapshot(document, model, 0, active) }
95
- }
96
-
97
- const indeterminate = (
98
- session: MachineSimulator.Session,
99
- event: string,
100
- transitions: ReadonlyArray<Transition>,
101
- reason: MachineSimulator.Indeterminate["reason"]
102
- ): MachineSimulator.Indeterminate => ({
103
- _tag: "Indeterminate",
104
- event,
105
- transitionIds: transitions.map((transition) => transition.id),
106
- session: session.snapshot,
107
- reason
108
- })
109
-
110
- const notesFor = (
111
- document: MachineDocument.MachineDocument,
112
- transition: Transition,
113
- nextActive: ReadonlyArray<string>
114
- ): ReadonlyArray<MachineSimulator.Note> => {
115
- const notes = new Set<MachineSimulator.Note>(["runtime-effects-skipped"])
116
- if (transition.reenter) notes.add("reentry-lifecycles-skipped")
117
- if (transition.branches.some((branch) => branch.updates.length > 0)) notes.add("state-updates-skipped")
118
- const active = new Set(nextActive)
119
- if (
120
- document.transitions.some((candidate) =>
121
- active.has(candidate.source) &&
122
- (candidate.trigger.type === "always" || candidate.trigger.type === "choice" || candidate.trigger.type === "done")
123
- )
124
- ) {
125
- notes.add("automatic-transitions-skipped")
126
- }
127
- return [...notes]
128
- }
129
-
130
- const nextConfiguration = (
131
- session: MachineSimulator.Session,
132
- transition: Transition,
133
- target: string | null
134
- ): MachineSimulator.Session | undefined => {
135
- const document = session.document
136
- const model = makeModel(document)
137
- if (target === null) {
138
- return {
139
- document,
140
- snapshot: snapshot(document, model, session.snapshot.step + 1, session.snapshot.activePaths)
141
- }
142
- }
143
- if (!model.states.has(target)) return undefined
144
-
145
- const naturalBoundary = leastCommonAncestor(model, transition.source, target)
146
- const sourceParent = model.states.get(transition.source)?.parent ?? null
147
- const boundary = transition.reenter
148
- ? sourceParent === null || naturalBoundary === null
149
- ? null
150
- : ancestors(model, naturalBoundary).length <= ancestors(model, sourceParent).length
151
- ? naturalBoundary
152
- : sourceParent
153
- : naturalBoundary
154
- const active = new Set(session.snapshot.activePaths)
155
- for (const path of active) {
156
- if (boundary === null || isDescendant(model, path, boundary)) active.delete(path)
157
- }
158
- ancestors(model, target).forEach((path) => {
159
- if (boundary === null || path === boundary || isDescendant(model, path, boundary)) active.add(path)
160
- })
161
- enter(model, target, active)
162
- return { document, snapshot: snapshot(document, model, session.snapshot.step + 1, active) }
163
- }
164
-
165
- export const send = (session: MachineSimulator.Session, event: string): MachineSimulator.StepResult => {
166
- const active = new Set(session.snapshot.activePaths)
167
- const transitions = session.document.transitions.filter((transition) =>
168
- transition.trigger.type === "event" && transition.trigger.event === event && active.has(transition.source)
169
- )
170
- if (transitions.length === 0) {
171
- return {
172
- _tag: "Blocked",
173
- event,
174
- transitionIds: [],
175
- session: session.snapshot,
176
- reason: "event-not-enabled"
177
- }
178
- }
179
- if (transitions.length > 1) return indeterminate(session, event, transitions, "multiple-transitions")
180
- const transition = transitions[0]!
181
- if (transition.acceptance === "declinable") {
182
- return indeterminate(session, event, transitions, "declinable-transition")
183
- }
184
- if (transition.branches.length !== 1 || transition.branches[0]?.type === "branch") {
185
- return indeterminate(session, event, transitions, "conditional-branches")
186
- }
187
- const branch = transition.branches[0]!
188
- if (branch.selection.kind === "history") return indeterminate(session, event, transitions, "history-target")
189
- if (branch.selection.kind === "choice") return indeterminate(session, event, transitions, "choice-target")
190
- const next = nextConfiguration(session, transition, branch.target)
191
- if (next === undefined) return indeterminate(session, event, transitions, "missing-target")
192
- return {
193
- _tag: "Applied",
194
- event,
195
- transitionIds: [transition.id],
196
- session: next.snapshot,
197
- notes: notesFor(session.document, transition, next.snapshot.activePaths)
198
- }
199
- }