@typeonce/effect-machine-devtools 0.23.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/LICENSE +21 -0
- package/NOTICE +3 -0
- package/README.md +81 -0
- package/dist/DevServer.d.ts +42 -0
- package/dist/DevServer.d.ts.map +1 -0
- package/dist/DevServer.js +28 -0
- package/dist/DevServer.js.map +1 -0
- package/dist/DevToolsProtocol.d.ts +954 -0
- package/dist/DevToolsProtocol.d.ts.map +1 -0
- package/dist/DevToolsProtocol.js +96 -0
- package/dist/DevToolsProtocol.js.map +1 -0
- package/dist/MachineDocument.d.ts +396 -0
- package/dist/MachineDocument.d.ts.map +1 -0
- package/dist/MachineDocument.js +172 -0
- package/dist/MachineDocument.js.map +1 -0
- package/dist/MachineRegistry.d.ts +349 -0
- package/dist/MachineRegistry.d.ts.map +1 -0
- package/dist/MachineRegistry.js +46 -0
- package/dist/MachineRegistry.js.map +1 -0
- package/dist/MachineSimulator.d.ts +169 -0
- package/dist/MachineSimulator.d.ts.map +1 -0
- package/dist/MachineSimulator.js +98 -0
- package/dist/MachineSimulator.js.map +1 -0
- package/dist/ProjectInspector.d.ts +116 -0
- package/dist/ProjectInspector.d.ts.map +1 -0
- package/dist/ProjectInspector.js +82 -0
- package/dist/ProjectInspector.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +23 -0
- package/dist/bin.js.map +1 -0
- package/dist/client/assets/index-BGOhE3Ng.css +1 -0
- package/dist/client/assets/index-DiqGhsGR.js +14 -0
- package/dist/client/index.html +14 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/devServer.d.ts +9 -0
- package/dist/internal/devServer.d.ts.map +1 -0
- package/dist/internal/devServer.js +119 -0
- package/dist/internal/devServer.js.map +1 -0
- package/dist/internal/evaluationWorker.d.ts +3 -0
- package/dist/internal/evaluationWorker.d.ts.map +1 -0
- package/dist/internal/evaluationWorker.js +69 -0
- package/dist/internal/evaluationWorker.js.map +1 -0
- package/dist/internal/evaluationWorkerBootstrap.d.ts +2 -0
- package/dist/internal/evaluationWorkerBootstrap.d.ts.map +1 -0
- package/dist/internal/evaluationWorkerBootstrap.js +21 -0
- package/dist/internal/evaluationWorkerBootstrap.js.map +1 -0
- package/dist/internal/machineDocument.d.ts +4 -0
- package/dist/internal/machineDocument.d.ts.map +1 -0
- package/dist/internal/machineDocument.js +122 -0
- package/dist/internal/machineDocument.js.map +1 -0
- package/dist/internal/machineRegistry.d.ts +12 -0
- package/dist/internal/machineRegistry.d.ts.map +1 -0
- package/dist/internal/machineRegistry.js +97 -0
- package/dist/internal/machineRegistry.js.map +1 -0
- package/dist/internal/machineSimulator.d.ts +5 -0
- package/dist/internal/machineSimulator.d.ts.map +1 -0
- package/dist/internal/machineSimulator.js +156 -0
- package/dist/internal/machineSimulator.js.map +1 -0
- package/dist/internal/projectInspector.d.ts +11 -0
- package/dist/internal/projectInspector.d.ts.map +1 -0
- package/dist/internal/projectInspector.js +150 -0
- package/dist/internal/projectInspector.js.map +1 -0
- package/index.html +13 -0
- package/package.json +69 -0
- package/src/DevServer.ts +45 -0
- package/src/DevToolsProtocol.ts +146 -0
- package/src/MachineDocument.ts +264 -0
- package/src/MachineRegistry.ts +69 -0
- package/src/MachineSimulator.ts +154 -0
- package/src/ProjectInspector.ts +140 -0
- package/src/bin.ts +56 -0
- package/src/index.ts +20 -0
- package/src/internal/browser/example-machine.ts +134 -0
- package/src/internal/browser/machine-index.ts +81 -0
- package/src/internal/browser/main.ts +42 -0
- package/src/internal/browser/styles.css +817 -0
- package/src/internal/browser/text-tree.ts +214 -0
- package/src/internal/browser/visualizer-app.ts +659 -0
- package/src/internal/browser/visualizer-model.ts +173 -0
- package/src/internal/browser/visualizer.ts +31 -0
- package/src/internal/devServer.ts +171 -0
- package/src/internal/evaluationWorker.ts +138 -0
- package/src/internal/evaluationWorkerBootstrap.js +21 -0
- package/src/internal/machineDocument.ts +138 -0
- package/src/internal/machineRegistry.ts +128 -0
- package/src/internal/machineSimulator.ts +199 -0
- package/src/internal/projectInspector.ts +226 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Activity as VisualizationActivity,
|
|
3
|
+
Branch as VisualizationBranch,
|
|
4
|
+
MachineDocument as VisualizationDocument,
|
|
5
|
+
State as VisualizationState,
|
|
6
|
+
Transition as VisualizationTransition
|
|
7
|
+
} from "../../MachineDocument.js"
|
|
8
|
+
|
|
9
|
+
export type TreeItemKind = "state" | "transition" | "branch" | "activity"
|
|
10
|
+
|
|
11
|
+
export interface TreeItemDetail {
|
|
12
|
+
readonly label: string
|
|
13
|
+
readonly value: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface TreeItem {
|
|
17
|
+
readonly id: string
|
|
18
|
+
readonly kind: TreeItemKind
|
|
19
|
+
readonly label: string
|
|
20
|
+
readonly details: ReadonlyArray<TreeItemDetail>
|
|
21
|
+
readonly children: ReadonlyArray<TreeItem>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface TextTree {
|
|
25
|
+
readonly machineId: string
|
|
26
|
+
readonly legend: string
|
|
27
|
+
readonly roots: ReadonlyArray<TreeItem>
|
|
28
|
+
readonly candidateEvents: ReadonlyArray<string> | undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const nodeLabel = (node: VisualizationState, active: ReadonlySet<string>): string => {
|
|
32
|
+
const status = active.has(node.path) ? "●" : "○"
|
|
33
|
+
const label = node.title === null ? node.key : `${node.title} (${node.key})`
|
|
34
|
+
const details: Array<string> = node.type === "atomic" ? [] : [node.type]
|
|
35
|
+
if (node.initial !== null) {
|
|
36
|
+
details.push(`initial: ${node.initial.slice(node.path.length + 1)}`)
|
|
37
|
+
}
|
|
38
|
+
if (node.history !== null) {
|
|
39
|
+
details.push(node.history)
|
|
40
|
+
}
|
|
41
|
+
return details.length === 0 ? `${status} ${label}` : `${status} ${label} [${details.join(", ")}]`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const triggerLabel = (definition: VisualizationTransition): string => {
|
|
45
|
+
const reenter = definition.reenter ? " [reenter]" : ""
|
|
46
|
+
const acceptance = definition.acceptance === "declinable" ? " [declinable]" : ""
|
|
47
|
+
switch (definition.trigger.type) {
|
|
48
|
+
case "event":
|
|
49
|
+
return `◇ on: ${definition.trigger.event}${reenter}${acceptance}`
|
|
50
|
+
case "always":
|
|
51
|
+
return `◇ always${reenter}${acceptance}`
|
|
52
|
+
case "done":
|
|
53
|
+
return `◇ done${reenter}${acceptance}`
|
|
54
|
+
case "choice":
|
|
55
|
+
return "◇ choice"
|
|
56
|
+
case "invoke":
|
|
57
|
+
return `◇ invoke ${definition.trigger.id} ${definition.trigger.outcome}${reenter}${acceptance}`
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const branchLabel = (branch: VisualizationBranch): string | undefined => {
|
|
62
|
+
if (branch.target === null && branch.updates.length > 0) {
|
|
63
|
+
return branch.type === "direct"
|
|
64
|
+
? `update ${branch.updates.join(", ")}`
|
|
65
|
+
: `[${branch.title}] update ${branch.updates.join(", ")}`
|
|
66
|
+
}
|
|
67
|
+
if (branch.target === null) return undefined
|
|
68
|
+
|
|
69
|
+
const target = branch.target.slice(branch.target.lastIndexOf(".") + 1)
|
|
70
|
+
const updates = branch.updates.length === 0 ? "" : ` / update ${branch.updates.join(", ")}`
|
|
71
|
+
return branch.type === "direct"
|
|
72
|
+
? `→ ${target}${updates}`
|
|
73
|
+
: `[${branch.title}] → ${target}${updates}`
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const activityLabel = (definition: VisualizationActivity): string => {
|
|
77
|
+
switch (definition.type) {
|
|
78
|
+
case "process":
|
|
79
|
+
return `◆ process: ${definition.lifecycleId}`
|
|
80
|
+
case "effect":
|
|
81
|
+
return `◆ effect: ${definition.lifecycleId} [success: ${definition.outcomes.success}, failure: ${definition.outcomes.failure}]`
|
|
82
|
+
case "timer":
|
|
83
|
+
return `◆ timer: ${definition.lifecycleId} [${definition.duration}]`
|
|
84
|
+
case "stream":
|
|
85
|
+
return `◆ stream: ${definition.lifecycleId}`
|
|
86
|
+
case "machine": {
|
|
87
|
+
const identity = definition.child.machineId ?? definition.child.id
|
|
88
|
+
return `◆ machine: ${definition.lifecycleId} → ${identity}`
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const activityDetails = (definition: VisualizationActivity): ReadonlyArray<TreeItemDetail> => {
|
|
94
|
+
const common: Array<TreeItemDetail> = [
|
|
95
|
+
{ label: "Owner", value: definition.source },
|
|
96
|
+
{ label: "Lifecycle id", value: definition.lifecycleId },
|
|
97
|
+
{ label: "Kind", value: definition.type }
|
|
98
|
+
]
|
|
99
|
+
if (definition.type === "timer") common.push({ label: "Duration", value: definition.duration })
|
|
100
|
+
if (definition.type === "machine") {
|
|
101
|
+
common.push({ label: "Child address", value: definition.child.id })
|
|
102
|
+
common.push({ label: "Machine", value: definition.child.machineId ?? "dynamic" })
|
|
103
|
+
}
|
|
104
|
+
return common
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const visualizationDocumentToTextTree = (document: VisualizationDocument): TextTree => {
|
|
108
|
+
const active = new Set(document.snapshot?.activePaths ?? [])
|
|
109
|
+
const states = new Map(document.states.map((state) => [state.path, state]))
|
|
110
|
+
const transitions = new Map(document.transitions.map((transition) => [transition.id, transition]))
|
|
111
|
+
const activities = new Map(document.activities.map((activity) => [activity.id, activity]))
|
|
112
|
+
const children = new Map<string | null, Array<VisualizationState>>()
|
|
113
|
+
|
|
114
|
+
for (const state of document.states) {
|
|
115
|
+
const siblings = children.get(state.parent) ?? []
|
|
116
|
+
siblings.push(state)
|
|
117
|
+
children.set(state.parent, siblings)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const visit = (state: VisualizationState): TreeItem => {
|
|
121
|
+
const transitionItems = state.transitionIds.flatMap((transitionId): ReadonlyArray<TreeItem> => {
|
|
122
|
+
const definition = transitions.get(transitionId)
|
|
123
|
+
if (definition === undefined) return []
|
|
124
|
+
const branchItems = definition.branches.flatMap((branch): ReadonlyArray<TreeItem> => {
|
|
125
|
+
const label = branchLabel(branch)
|
|
126
|
+
if (label === undefined) return []
|
|
127
|
+
return [{
|
|
128
|
+
id: branch.id,
|
|
129
|
+
kind: "branch",
|
|
130
|
+
label,
|
|
131
|
+
details: [
|
|
132
|
+
{ label: "Source", value: definition.source },
|
|
133
|
+
{ label: "Target", value: branch.target ?? "none" },
|
|
134
|
+
{ label: "Selection", value: branch.selection.kind },
|
|
135
|
+
{ label: "Scope", value: branch.selection.scope ?? "none" },
|
|
136
|
+
{ label: "Updates", value: branch.updates.length === 0 ? "none" : branch.updates.join(", ") }
|
|
137
|
+
],
|
|
138
|
+
children: []
|
|
139
|
+
}]
|
|
140
|
+
})
|
|
141
|
+
return [{
|
|
142
|
+
id: definition.id,
|
|
143
|
+
kind: "transition",
|
|
144
|
+
label: triggerLabel(definition),
|
|
145
|
+
details: [
|
|
146
|
+
{ label: "Source", value: definition.source },
|
|
147
|
+
{ label: "Acceptance", value: definition.acceptance },
|
|
148
|
+
{ label: "Reenter", value: definition.reenter ? "yes" : "no" },
|
|
149
|
+
{ label: "Branches", value: String(branchItems.length) }
|
|
150
|
+
],
|
|
151
|
+
children: branchItems
|
|
152
|
+
}]
|
|
153
|
+
})
|
|
154
|
+
const activityItems = state.activityIds.flatMap((activityId): ReadonlyArray<TreeItem> => {
|
|
155
|
+
const definition = activities.get(activityId)
|
|
156
|
+
return definition === undefined ? [] : [{
|
|
157
|
+
id: definition.id,
|
|
158
|
+
kind: "activity",
|
|
159
|
+
label: activityLabel(definition),
|
|
160
|
+
details: activityDetails(definition),
|
|
161
|
+
children: []
|
|
162
|
+
}]
|
|
163
|
+
})
|
|
164
|
+
const descendantItems = (children.get(state.path) ?? []).map(visit)
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
id: state.path,
|
|
168
|
+
kind: "state",
|
|
169
|
+
label: nodeLabel(state, active),
|
|
170
|
+
details: [
|
|
171
|
+
{ label: "Path", value: state.path },
|
|
172
|
+
{ label: "Type", value: state.type },
|
|
173
|
+
{ label: "Status", value: active.has(state.path) ? "active" : "inactive" },
|
|
174
|
+
{ label: "Parent", value: state.parent ?? "root" },
|
|
175
|
+
{ label: "Initial", value: state.initial ?? "none" },
|
|
176
|
+
{ label: "Children", value: String(state.children.length) }
|
|
177
|
+
],
|
|
178
|
+
children: [...transitionItems, ...activityItems, ...descendantItems]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
machineId: document.machineId,
|
|
184
|
+
legend: document.activities.length === 0
|
|
185
|
+
? "● active ○ inactive ◇ transition ┄ branch → target"
|
|
186
|
+
: "● active ○ inactive ◇ transition ┄ branch → target ◆ activity",
|
|
187
|
+
roots: document.roots.flatMap((path): ReadonlyArray<TreeItem> => {
|
|
188
|
+
const state = states.get(path)
|
|
189
|
+
return state === undefined ? [] : [visit(state)]
|
|
190
|
+
}),
|
|
191
|
+
candidateEvents: document.snapshot?.candidateEvents
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const textConnector = (kind: TreeItemKind, isLast: boolean): string => {
|
|
196
|
+
if (kind === "branch") return isLast ? "└┄" : "├┄"
|
|
197
|
+
return isLast ? "└─" : "├─"
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export const textTreeToString = (document: VisualizationDocument): string => {
|
|
201
|
+
const tree = visualizationDocumentToTextTree(document)
|
|
202
|
+
const lines = [tree.machineId, tree.legend, ""]
|
|
203
|
+
const visit = (item: TreeItem, prefix: string, isLast: boolean): void => {
|
|
204
|
+
lines.push(`${prefix}${textConnector(item.kind, isLast)} ${item.label}`)
|
|
205
|
+
const childPrefix = `${prefix}${isLast ? " " : "│ "}`
|
|
206
|
+
item.children.forEach((child, index) => visit(child, childPrefix, index === item.children.length - 1))
|
|
207
|
+
}
|
|
208
|
+
tree.roots.forEach((item, index) => visit(item, "", index === tree.roots.length - 1))
|
|
209
|
+
if (tree.candidateEvents !== undefined) {
|
|
210
|
+
const candidates = tree.candidateEvents.length === 0 ? "none" : tree.candidateEvents.join(", ")
|
|
211
|
+
lines.push("", `Candidate events: ${candidates}`)
|
|
212
|
+
}
|
|
213
|
+
return lines.join("\n")
|
|
214
|
+
}
|