@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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +3 -0
  3. package/README.md +81 -0
  4. package/dist/DevServer.d.ts +42 -0
  5. package/dist/DevServer.d.ts.map +1 -0
  6. package/dist/DevServer.js +28 -0
  7. package/dist/DevServer.js.map +1 -0
  8. package/dist/DevToolsProtocol.d.ts +954 -0
  9. package/dist/DevToolsProtocol.d.ts.map +1 -0
  10. package/dist/DevToolsProtocol.js +96 -0
  11. package/dist/DevToolsProtocol.js.map +1 -0
  12. package/dist/MachineDocument.d.ts +396 -0
  13. package/dist/MachineDocument.d.ts.map +1 -0
  14. package/dist/MachineDocument.js +172 -0
  15. package/dist/MachineDocument.js.map +1 -0
  16. package/dist/MachineRegistry.d.ts +349 -0
  17. package/dist/MachineRegistry.d.ts.map +1 -0
  18. package/dist/MachineRegistry.js +46 -0
  19. package/dist/MachineRegistry.js.map +1 -0
  20. package/dist/MachineSimulator.d.ts +169 -0
  21. package/dist/MachineSimulator.d.ts.map +1 -0
  22. package/dist/MachineSimulator.js +98 -0
  23. package/dist/MachineSimulator.js.map +1 -0
  24. package/dist/ProjectInspector.d.ts +116 -0
  25. package/dist/ProjectInspector.d.ts.map +1 -0
  26. package/dist/ProjectInspector.js +82 -0
  27. package/dist/ProjectInspector.js.map +1 -0
  28. package/dist/bin.d.ts +3 -0
  29. package/dist/bin.d.ts.map +1 -0
  30. package/dist/bin.js +23 -0
  31. package/dist/bin.js.map +1 -0
  32. package/dist/client/assets/index-BGOhE3Ng.css +1 -0
  33. package/dist/client/assets/index-DiqGhsGR.js +14 -0
  34. package/dist/client/index.html +14 -0
  35. package/dist/index.d.ts +18 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +18 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/internal/devServer.d.ts +9 -0
  40. package/dist/internal/devServer.d.ts.map +1 -0
  41. package/dist/internal/devServer.js +119 -0
  42. package/dist/internal/devServer.js.map +1 -0
  43. package/dist/internal/evaluationWorker.d.ts +3 -0
  44. package/dist/internal/evaluationWorker.d.ts.map +1 -0
  45. package/dist/internal/evaluationWorker.js +69 -0
  46. package/dist/internal/evaluationWorker.js.map +1 -0
  47. package/dist/internal/evaluationWorkerBootstrap.d.ts +2 -0
  48. package/dist/internal/evaluationWorkerBootstrap.d.ts.map +1 -0
  49. package/dist/internal/evaluationWorkerBootstrap.js +21 -0
  50. package/dist/internal/evaluationWorkerBootstrap.js.map +1 -0
  51. package/dist/internal/machineDocument.d.ts +4 -0
  52. package/dist/internal/machineDocument.d.ts.map +1 -0
  53. package/dist/internal/machineDocument.js +122 -0
  54. package/dist/internal/machineDocument.js.map +1 -0
  55. package/dist/internal/machineRegistry.d.ts +12 -0
  56. package/dist/internal/machineRegistry.d.ts.map +1 -0
  57. package/dist/internal/machineRegistry.js +97 -0
  58. package/dist/internal/machineRegistry.js.map +1 -0
  59. package/dist/internal/machineSimulator.d.ts +5 -0
  60. package/dist/internal/machineSimulator.d.ts.map +1 -0
  61. package/dist/internal/machineSimulator.js +156 -0
  62. package/dist/internal/machineSimulator.js.map +1 -0
  63. package/dist/internal/projectInspector.d.ts +11 -0
  64. package/dist/internal/projectInspector.d.ts.map +1 -0
  65. package/dist/internal/projectInspector.js +150 -0
  66. package/dist/internal/projectInspector.js.map +1 -0
  67. package/index.html +13 -0
  68. package/package.json +69 -0
  69. package/src/DevServer.ts +45 -0
  70. package/src/DevToolsProtocol.ts +146 -0
  71. package/src/MachineDocument.ts +264 -0
  72. package/src/MachineRegistry.ts +69 -0
  73. package/src/MachineSimulator.ts +154 -0
  74. package/src/ProjectInspector.ts +140 -0
  75. package/src/bin.ts +56 -0
  76. package/src/index.ts +20 -0
  77. package/src/internal/browser/example-machine.ts +134 -0
  78. package/src/internal/browser/machine-index.ts +81 -0
  79. package/src/internal/browser/main.ts +42 -0
  80. package/src/internal/browser/styles.css +817 -0
  81. package/src/internal/browser/text-tree.ts +214 -0
  82. package/src/internal/browser/visualizer-app.ts +659 -0
  83. package/src/internal/browser/visualizer-model.ts +173 -0
  84. package/src/internal/browser/visualizer.ts +31 -0
  85. package/src/internal/devServer.ts +171 -0
  86. package/src/internal/evaluationWorker.ts +138 -0
  87. package/src/internal/evaluationWorkerBootstrap.js +21 -0
  88. package/src/internal/machineDocument.ts +138 -0
  89. package/src/internal/machineRegistry.ts +128 -0
  90. package/src/internal/machineSimulator.ts +199 -0
  91. 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
+ }