@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.
- package/NOTICE +4 -0
- package/README.md +14 -8
- package/dist/DevServer.d.ts +2 -1
- package/dist/DevServer.d.ts.map +1 -1
- package/dist/DevServer.js.map +1 -1
- package/dist/DevToolsProtocol.d.ts +167 -17
- package/dist/DevToolsProtocol.d.ts.map +1 -1
- package/dist/DevToolsProtocol.js +1 -1
- package/dist/MachineDocument.d.ts +98 -2
- package/dist/MachineDocument.d.ts.map +1 -1
- package/dist/MachineDocument.js +35 -1
- package/dist/MachineDocument.js.map +1 -1
- package/dist/MachineRegistry.d.ts +56 -6
- package/dist/MachineRegistry.d.ts.map +1 -1
- package/dist/MachineWalkthrough.d.ts +171 -0
- package/dist/MachineWalkthrough.d.ts.map +1 -0
- package/dist/MachineWalkthrough.js +98 -0
- package/dist/MachineWalkthrough.js.map +1 -0
- package/dist/client/assets/index-BTOAhezX.js +37 -0
- package/dist/client/assets/index-Cg7xzSkz.css +1 -0
- package/dist/client/index.html +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/devServer.d.ts +2 -1
- package/dist/internal/devServer.d.ts.map +1 -1
- package/dist/internal/devServer.js +4 -3
- package/dist/internal/devServer.js.map +1 -1
- package/dist/internal/evaluationWorker.d.ts.map +1 -1
- package/dist/internal/evaluationWorker.js +28 -9
- package/dist/internal/evaluationWorker.js.map +1 -1
- package/dist/internal/machineDocument.d.ts.map +1 -1
- package/dist/internal/machineDocument.js +65 -2
- package/dist/internal/machineDocument.js.map +1 -1
- package/dist/internal/machineWalkthrough.d.ts +26 -0
- package/dist/internal/machineWalkthrough.d.ts.map +1 -0
- package/dist/internal/machineWalkthrough.js +256 -0
- package/dist/internal/machineWalkthrough.js.map +1 -0
- package/dist/internal/projectInspector.d.ts.map +1 -1
- package/dist/internal/projectInspector.js +15 -9
- package/dist/internal/projectInspector.js.map +1 -1
- package/index.html +2 -2
- package/package.json +4 -3
- package/src/DevServer.ts +6 -2
- package/src/DevToolsProtocol.ts +1 -1
- package/src/MachineDocument.ts +56 -1
- package/src/MachineWalkthrough.ts +199 -0
- package/src/index.ts +2 -2
- package/src/internal/browser/chart-layout.ts +433 -0
- package/src/internal/browser/chart-model.ts +234 -0
- package/src/internal/browser/chart-renderer.ts +661 -0
- package/src/internal/browser/example-machine.ts +15 -6
- package/src/internal/browser/input-form.ts +252 -0
- package/src/internal/browser/invoke-outcomes-example.ts +231 -0
- package/src/internal/browser/parallel-completion-example.ts +199 -0
- package/src/internal/browser/planner-example.ts +145 -0
- package/src/internal/browser/protocol-events-example.ts +200 -0
- package/src/internal/browser/styles.css +959 -185
- package/src/internal/browser/transition-semantics-example.ts +236 -0
- package/src/internal/browser/visualizer-app.ts +818 -389
- package/src/internal/browser/visualizer-model.ts +104 -0
- package/src/internal/browser/visualizer.ts +1 -1
- package/src/internal/devServer.ts +9 -4
- package/src/internal/evaluationWorker.ts +28 -10
- package/src/internal/machineDocument.ts +77 -2
- package/src/internal/machineWalkthrough.ts +355 -0
- package/src/internal/projectInspector.ts +29 -14
- package/dist/MachineSimulator.d.ts +0 -169
- package/dist/MachineSimulator.d.ts.map +0 -1
- package/dist/MachineSimulator.js +0 -98
- package/dist/MachineSimulator.js.map +0 -1
- package/dist/client/assets/index-BGOhE3Ng.css +0 -1
- package/dist/client/assets/index-DiqGhsGR.js +0 -14
- package/dist/internal/machineSimulator.d.ts +0 -5
- package/dist/internal/machineSimulator.d.ts.map +0 -1
- package/dist/internal/machineSimulator.js +0 -156
- package/dist/internal/machineSimulator.js.map +0 -1
- package/src/MachineSimulator.ts +0 -154
- package/src/internal/machineSimulator.ts +0 -199
package/src/MachineSimulator.ts
DELETED
|
@@ -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
|
-
}
|