@kiberon-labs/behave-graph-flow 1.0.0 → 2.0.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 (314) hide show
  1. package/.fallowrc.json +16 -0
  2. package/.storybook/main.ts +32 -0
  3. package/.storybook/preview.ts +16 -0
  4. package/.storybook/styles.css +10 -0
  5. package/.storybook/vscode.css +814 -0
  6. package/.turbo/turbo-build.log +7 -0
  7. package/LICENSE +6 -0
  8. package/README.md +2 -2
  9. package/data/Polynomial.json +510 -0
  10. package/data/sequence.json +337 -0
  11. package/data/trigger-event.json +241 -0
  12. package/data/variable-change.json +210 -0
  13. package/dist/entry.css +4 -0
  14. package/dist/index.css +39 -0
  15. package/dist/index.css.map +1 -0
  16. package/dist/index.d.ts +2282 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +14873 -0
  19. package/dist/index.js.map +1 -0
  20. package/docs/notifications.md +246 -0
  21. package/docs/protocol.md +679 -0
  22. package/docs/specifics.md +191 -0
  23. package/package.json +85 -21
  24. package/postcss.config.ts +3 -4
  25. package/src/annotations/index.ts +32 -0
  26. package/src/components/FloatingToolbar/index.module.css +45 -0
  27. package/src/components/FloatingToolbar/index.tsx +256 -0
  28. package/src/components/Flow.tsx +276 -75
  29. package/src/components/contextMenus/NodePicker.module.css +274 -0
  30. package/src/components/contextMenus/NodePicker.tsx +481 -0
  31. package/src/components/contextMenus/edge.tsx +108 -0
  32. package/src/components/contextMenus/node.tsx +155 -0
  33. package/src/components/contextMenus/selection.tsx +77 -0
  34. package/src/components/controls/any/index.tsx +8 -0
  35. package/src/components/controls/boolean/index.tsx +13 -0
  36. package/src/components/controls/colorPicker/InputPopover.module.css +100 -0
  37. package/src/components/controls/colorPicker/InputPopover.tsx +31 -0
  38. package/src/components/controls/colorPicker/index.module.css +18 -0
  39. package/src/components/controls/colorPicker/index.tsx +61 -0
  40. package/src/components/controls/number/index.tsx +35 -0
  41. package/src/components/controls/string/index.tsx +16 -0
  42. package/src/components/edges/index.tsx +469 -0
  43. package/src/components/edges/offsetBezier.ts +134 -0
  44. package/src/components/hotKeys.tsx +20 -0
  45. package/src/components/layoutController/index.module.css +10 -0
  46. package/src/components/layoutController/index.tsx +117 -0
  47. package/src/components/layoutController/utils.ts +205 -0
  48. package/src/components/menubar/defaults.tsx +480 -0
  49. package/src/components/menubar/index.tsx +49 -0
  50. package/src/components/menubar/menuItem.module.css +16 -0
  51. package/src/components/menubar/menuItem.tsx +32 -0
  52. package/src/components/nodes/behave/Node.module.css +23 -0
  53. package/src/components/nodes/behave/Node.tsx +176 -0
  54. package/src/components/nodes/behave/NodeContainer.module.css +87 -0
  55. package/src/components/nodes/behave/NodeContainer.tsx +46 -0
  56. package/src/components/nodes/behave/index.tsx +14 -0
  57. package/src/components/nodes/comment/FormatToolbar.tsx +118 -0
  58. package/src/components/nodes/comment/comment.tsx +103 -0
  59. package/src/components/nodes/comment/styles.module.css +150 -0
  60. package/src/components/nodes/group/index.tsx +109 -0
  61. package/src/components/nodes/wrapper/index.tsx +73 -0
  62. package/src/components/nodes/wrapper/styles.module.css +113 -0
  63. package/src/components/notifications/NotificationProvider.tsx +81 -0
  64. package/src/components/notifications/index.ts +2 -0
  65. package/src/components/notifications/utils.ts +71 -0
  66. package/src/components/panels/alignment/index.module.css +20 -0
  67. package/src/components/panels/alignment/index.tsx +244 -0
  68. package/src/components/panels/base/index.tsx +5 -0
  69. package/src/components/panels/base/styles.module.css +12 -0
  70. package/src/components/panels/conversation/index.module.css +151 -0
  71. package/src/components/panels/conversation/index.tsx +162 -0
  72. package/src/components/panels/events/CustomEventsEditor.tsx +384 -0
  73. package/src/components/panels/events/EditEventPanel.tsx +315 -0
  74. package/src/components/panels/events/ManageEventsPanel.tsx +98 -0
  75. package/src/components/panels/events/index.tsx +23 -0
  76. package/src/components/panels/events/styles.module.css +236 -0
  77. package/src/components/panels/history/index.tsx +92 -0
  78. package/src/components/panels/history/styles.module.css +106 -0
  79. package/src/components/panels/keymaps/index.module.css +78 -0
  80. package/src/components/panels/keymaps/index.tsx +167 -0
  81. package/src/components/panels/layers/index.tsx +240 -0
  82. package/src/components/panels/layers/styles.module.css +110 -0
  83. package/src/components/panels/legend/index.module.css +6 -0
  84. package/src/components/panels/legend/index.tsx +76 -0
  85. package/src/components/panels/logs/index.module.css +212 -0
  86. package/src/components/panels/logs/index.tsx +288 -0
  87. package/src/components/panels/nodeInputs/InputControl.tsx +63 -0
  88. package/src/components/panels/nodeInputs/InputsGroup.tsx +64 -0
  89. package/src/components/panels/nodeInputs/MultipleNodesView.tsx +37 -0
  90. package/src/components/panels/nodeInputs/NodeSettings.tsx +92 -0
  91. package/src/components/panels/nodeInputs/NodeTitleEditor.tsx +125 -0
  92. package/src/components/panels/nodeInputs/OutputsGroup.tsx +65 -0
  93. package/src/components/panels/nodeInputs/SocketGenerators.tsx +32 -0
  94. package/src/components/panels/nodeInputs/index.module.css +284 -0
  95. package/src/components/panels/nodeInputs/index.tsx +339 -0
  96. package/src/components/panels/nodeInputs/useNodeHandlers.ts +76 -0
  97. package/src/components/panels/nodeInputs/useNodeInputsData.ts +173 -0
  98. package/src/components/panels/nodePicker/index.tsx +115 -0
  99. package/src/components/panels/panel/index.module.css +66 -0
  100. package/src/components/panels/panel/index.tsx +88 -0
  101. package/src/components/panels/search/index.module.css +66 -0
  102. package/src/components/panels/search/index.tsx +215 -0
  103. package/src/components/panels/systemSettings/index.tsx +206 -0
  104. package/src/components/panels/systemSettings/styles.module.css +11 -0
  105. package/src/components/panels/traces/GridLines.tsx +38 -0
  106. package/src/components/panels/traces/TimeGrid.tsx +48 -0
  107. package/src/components/panels/traces/TraceLane.tsx +62 -0
  108. package/src/components/panels/traces/TraceTooltip.tsx +22 -0
  109. package/src/components/panels/traces/TracesHeader.tsx +56 -0
  110. package/src/components/panels/traces/index.module.css +166 -0
  111. package/src/components/panels/traces/index.tsx +294 -0
  112. package/src/components/panels/traces/types.ts +48 -0
  113. package/src/components/panels/traces/useDerivedSpans.ts +212 -0
  114. package/src/components/panels/traces/utils.ts +25 -0
  115. package/src/components/panels/variables/CreateVariableScreen.tsx +162 -0
  116. package/src/components/panels/variables/ManageVariablesScreen.tsx +144 -0
  117. package/src/components/panels/variables/index.tsx +125 -0
  118. package/src/components/panels/variables/styles.module.css +236 -0
  119. package/src/components/primitives/icon.module.css +45 -0
  120. package/src/components/primitives/icon.tsx +38 -0
  121. package/src/components/sockets/input/index.tsx +76 -0
  122. package/src/components/sockets/input/styles.module.css +27 -0
  123. package/src/components/sockets/output/index.tsx +61 -0
  124. package/src/components/sockets/output/styles.module.css +27 -0
  125. package/src/css/prosemirror.css +57 -0
  126. package/src/css/rc-dock.css +112 -0
  127. package/src/css/rc-menu.css +100 -0
  128. package/src/css/vars.css +14 -0
  129. package/src/css/vscode.css +13 -0
  130. package/src/entry.css +4 -0
  131. package/src/generators/CustomEventOnTriggeredGenerator.tsx +85 -0
  132. package/src/generators/SequenceGenerator.tsx +104 -0
  133. package/src/generators/SwitchOnIntegerGenerator.tsx +256 -0
  134. package/src/generators/SwitchOnStringGenerator.tsx +263 -0
  135. package/src/generators/registerDefaultGenerators.ts +34 -0
  136. package/src/hooks/useBehaveGraphFlow.ts +17 -16
  137. package/src/hooks/useDetachNodes.ts +39 -0
  138. package/src/hooks/useFlowHandlers.ts +115 -29
  139. package/src/hooks/useWasdPan.ts +188 -0
  140. package/src/index.css +146 -0
  141. package/src/index.ts +36 -18
  142. package/src/layout/dagre.tsx +119 -0
  143. package/src/layout/elk.ts +200 -0
  144. package/src/plugin/alignment/index.ts +81 -0
  145. package/src/plugin/docs/index.tsx +299 -0
  146. package/src/plugin/docs/panel/index.tsx +200 -0
  147. package/src/plugin/docs/panel/styles.module.css +174 -0
  148. package/src/plugin/graphrunner/actions.ts +253 -0
  149. package/src/plugin/graphrunner/buttons.tsx +87 -0
  150. package/src/plugin/graphrunner/client.ts +704 -0
  151. package/src/plugin/graphrunner/index.tsx +255 -0
  152. package/src/plugin/graphrunner/panel.tsx +386 -0
  153. package/src/plugin/graphrunner/runner.ts +358 -0
  154. package/src/plugin/graphrunner/session.ts +243 -0
  155. package/src/plugin/graphrunner/store.ts +206 -0
  156. package/src/plugin/graphrunner/styles.module.css +211 -0
  157. package/src/plugin/graphrunner/transport.ts +224 -0
  158. package/src/plugin/graphrunner/types.ts +672 -0
  159. package/src/plugin/graphrunner-local/execution-utils.ts +457 -0
  160. package/src/plugin/graphrunner-local/index.tsx +166 -0
  161. package/src/plugin/graphrunner-local/panel.tsx +231 -0
  162. package/src/plugin/graphrunner-local/store.ts +41 -0
  163. package/src/plugin/graphrunner-local/styles.module.css +101 -0
  164. package/src/plugin/graphrunner-local/transport.ts +1372 -0
  165. package/src/plugin/graphrunner-local/types.ts +10 -0
  166. package/src/plugin/graphrunner-webworker/graph-executor.worker.ts +633 -0
  167. package/src/plugin/graphrunner-webworker/index.tsx +146 -0
  168. package/src/plugin/graphrunner-webworker/panel.tsx +173 -0
  169. package/src/plugin/graphrunner-webworker/store.ts +89 -0
  170. package/src/plugin/graphrunner-webworker/types.ts +17 -0
  171. package/src/plugin/graphrunner-webworker/worker-transport.ts +123 -0
  172. package/src/plugin/realtime/realtimeRunner.ts +570 -0
  173. package/src/specifics/CustomEventOnTriggeredSpecific.tsx +92 -0
  174. package/src/specifics/CustomEventTriggerSpecific.tsx +141 -0
  175. package/src/specifics/VariableGetSpecific.tsx +110 -0
  176. package/src/specifics/VariableSetSpecific.tsx +110 -0
  177. package/src/specifics/registerDefaultSpecifics.ts +5 -0
  178. package/src/store/actions.tsx +698 -0
  179. package/src/store/chat.ts +73 -0
  180. package/src/store/controls.tsx +62 -0
  181. package/src/store/documentation.tsx +69 -0
  182. package/src/store/events.tsx +116 -0
  183. package/src/store/flow.tsx +245 -0
  184. package/src/store/graphRunnerClient.ts +110 -0
  185. package/src/store/hotKeys.tsx +323 -0
  186. package/src/store/layers.ts +259 -0
  187. package/src/store/legend.tsx +76 -0
  188. package/src/store/logs.ts +28 -0
  189. package/src/store/menubar.ts +41 -0
  190. package/src/store/refs.ts +84 -0
  191. package/src/store/registry.ts +43 -0
  192. package/src/store/selection.ts +22 -0
  193. package/src/store/settings.ts +99 -0
  194. package/src/store/socketGenerator.tsx +54 -0
  195. package/src/store/specific.tsx +75 -0
  196. package/src/store/specs.tsx +35 -0
  197. package/src/store/tabs.ts +278 -0
  198. package/src/store/toolbar.tsx +45 -0
  199. package/src/store/traces.ts +240 -0
  200. package/src/store/variables.ts +37 -0
  201. package/src/system/graph.ts +134 -0
  202. package/src/system/index.ts +3 -0
  203. package/src/system/notifications.ts +98 -0
  204. package/src/system/plugin.ts +27 -0
  205. package/src/system/provider.tsx +22 -0
  206. package/src/system/pubsub.ts +323 -0
  207. package/src/system/system.ts +223 -0
  208. package/src/system/tabLoader.tsx +265 -0
  209. package/src/system/undoRedo.ts +103 -0
  210. package/src/transformers/Uigraph.ts +60 -0
  211. package/src/transformers/behaveToFlow.ts +16 -4
  212. package/src/transformers/flowToBehave.ts +32 -12
  213. package/src/types/NodeMetadata.ts +27 -0
  214. package/src/types/graph.ts +49 -0
  215. package/src/types/nodes.ts +45 -0
  216. package/src/types.ts +16 -0
  217. package/src/util/colors.ts +1 -29
  218. package/src/util/downloadJson.ts +18 -0
  219. package/src/util/extractNodeMetadata.ts +16 -0
  220. package/src/util/getPickerFilters.ts +1 -1
  221. package/src/util/isBehaveNode.ts +6 -0
  222. package/src/util/isValidConnection.ts +28 -15
  223. package/src/util/mergeSockets.ts +29 -0
  224. package/src/util/serializeVariables.ts +66 -0
  225. package/src/util/sockets.ts +43 -0
  226. package/stories/apex/layoutController/example-graph.worker.ts +39 -0
  227. package/stories/apex/layoutController/index.stories.tsx +48 -0
  228. package/stories/apex/layoutController/webworker.stories.tsx +103 -0
  229. package/stories/apex/menubar/menubar.stories.tsx +19 -0
  230. package/stories/components/colorpicker/index.stories.tsx +20 -0
  231. package/stories/components/contextMenus/edge.stories.tsx +32 -0
  232. package/stories/components/contextMenus/node.stories.tsx +26 -0
  233. package/stories/components/contextMenus/nodePicker.stories.tsx +115 -0
  234. package/stories/components/controls/any/index.stories.tsx +19 -0
  235. package/stories/components/controls/boolean/index.stories.tsx +19 -0
  236. package/stories/components/controls/colorPicker/index.stories.tsx +49 -0
  237. package/stories/components/controls/number/index.stories.tsx +19 -0
  238. package/stories/components/controls/string/index.stories.tsx +19 -0
  239. package/stories/components/nodes/behaveNode.stories.tsx +108 -0
  240. package/stories/components/nodes/comment.stories.tsx +106 -0
  241. package/stories/components/panels/alignment.stories.tsx +24 -0
  242. package/stories/components/panels/events.stories.tsx +38 -0
  243. package/stories/components/panels/graphRunner.stories.tsx +317 -0
  244. package/stories/components/panels/history.stories.tsx +37 -0
  245. package/stories/components/panels/keymaps.stories.tsx +21 -0
  246. package/stories/components/panels/legend.stories.tsx +37 -0
  247. package/stories/components/panels/logs.stories.tsx +24 -0
  248. package/stories/components/panels/nodeInputs.stories.tsx +21 -0
  249. package/stories/components/panels/nodePicker.stories.tsx +37 -0
  250. package/stories/components/panels/panel.stories.tsx +39 -0
  251. package/stories/components/panels/search.stories.tsx +24 -0
  252. package/stories/components/panels/systemSettings.stories.tsx +26 -0
  253. package/stories/components/panels/traces.stories.tsx +225 -0
  254. package/stories/components/panels/variables.stories.tsx +24 -0
  255. package/stories/defaults/defaultStoryProvider.tsx +167 -0
  256. package/stories/defaults/systemGenerator.ts +38 -0
  257. package/tests/components/edges/offsetBezier.test.ts +51 -0
  258. package/tests/components/layoutController/utils.test.ts +68 -0
  259. package/tests/components/panels/traces/utils.test.ts +52 -0
  260. package/tests/flowToBehave.test.ts +26 -4
  261. package/tests/notifications.test.ts +87 -0
  262. package/tests/saveLoad.test.ts +372 -0
  263. package/tests/util/calculateNewEdge.test.ts +98 -0
  264. package/tests/util/getSocketsByNodeTypeAndHandleType.test.ts +31 -0
  265. package/tests/util/hasPositionMetaData.test.ts +33 -0
  266. package/tests/util/isBehaveNode.test.ts +22 -0
  267. package/tests/util/isHandleConnected.test.ts +37 -0
  268. package/tests/util/mergeSockets.test.ts +43 -0
  269. package/tests/visual/README.md +64 -0
  270. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-alignment-chromium-win32.png +0 -0
  271. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-conversation-chromium-win32.png +0 -0
  272. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-events-chromium-win32.png +0 -0
  273. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-history-chromium-win32.png +0 -0
  274. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-keymaps-chromium-win32.png +0 -0
  275. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-layers-chromium-win32.png +0 -0
  276. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-legend-chromium-win32.png +0 -0
  277. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-logs-chromium-win32.png +0 -0
  278. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodeInputs-chromium-win32.png +0 -0
  279. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-nodePicker-chromium-win32.png +0 -0
  280. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-panel-chromium-win32.png +0 -0
  281. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-search-chromium-win32.png +0 -0
  282. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-systemSettings-chromium-win32.png +0 -0
  283. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-traces-chromium-win32.png +0 -0
  284. package/tests/visual/__screenshots__/panels.visual.test.tsx/panel-variables-chromium-win32.png +0 -0
  285. package/tests/visual/panels.visual.test.tsx +76 -0
  286. package/tsconfig.base.json +39 -0
  287. package/tsconfig.json +18 -59
  288. package/tsconfig.prod.json +23 -0
  289. package/tsdown.config.ts +15 -3
  290. package/typedoc.json +7 -7
  291. package/vite.config.js +7 -0
  292. package/vitest.config.ts +5 -2
  293. package/vitest.visual.config.ts +48 -0
  294. package/src/components/AutoSizeInput.tsx +0 -65
  295. package/src/components/Controls.tsx +0 -87
  296. package/src/components/InputSocket.tsx +0 -142
  297. package/src/components/Node.tsx +0 -68
  298. package/src/components/NodeContainer.tsx +0 -46
  299. package/src/components/NodePicker.tsx +0 -77
  300. package/src/components/OutputSocket.tsx +0 -58
  301. package/src/components/modals/ClearModal.tsx +0 -40
  302. package/src/components/modals/HelpModal.tsx +0 -36
  303. package/src/components/modals/LoadModal.tsx +0 -96
  304. package/src/components/modals/Modal.tsx +0 -64
  305. package/src/components/modals/SaveModal.tsx +0 -60
  306. package/src/hooks/useCustomNodeTypes.tsx +0 -31
  307. package/src/hooks/useGraphRunner.ts +0 -104
  308. package/src/hooks/useMergeMap.ts +0 -14
  309. package/src/hooks/useNodeSpecJson.ts +0 -20
  310. package/src/hooks/useQueriableDefinitions.ts +0 -22
  311. package/src/styles.css +0 -8
  312. package/tailwind.config.ts +0 -19
  313. package/tests/tsconfig.json +0 -10
  314. /package/src/{types.d.ts → types-declarations.d.ts} +0 -0
@@ -0,0 +1,457 @@
1
+ /**
2
+ * Shared graph execution utilities for both local and worker runners
3
+ */
4
+
5
+ import {
6
+ Engine,
7
+ type GraphInstance,
8
+ type ILifecycleEventEmitter,
9
+ readGraphFromJSON,
10
+ validateGraph,
11
+ ManualLifecycleEventEmitter,
12
+ DefaultLogger,
13
+ type ILogger,
14
+ type IRegistry,
15
+ writeNodeSpecsToJSON,
16
+ type NodeSpecJSON,
17
+ type GraphJSON
18
+ } from '@kiberon-labs/behave-graph';
19
+ import type {
20
+ GraphRunnerCapabilities,
21
+ RunStatus,
22
+ ServerGraphRunnerMessage,
23
+ ServerVariable,
24
+ ServerEvent
25
+ } from '../graphrunner/types.js';
26
+ import { sleep } from '@kiberon-labs/behave-graph';
27
+
28
+ export interface ActiveRun {
29
+ runId: string;
30
+ graphId: string;
31
+ engine: Engine;
32
+ graphInstance: GraphInstance;
33
+ registry: IRegistry;
34
+ status: RunStatus;
35
+ startedAt: number;
36
+ performance: {
37
+ nodesExecuted: number;
38
+ eventsEmitted: number;
39
+ variableChanges: number;
40
+ };
41
+ isPaused: boolean;
42
+ executionPhase: 'start' | 'tick' | 'end' | 'completed';
43
+ currentTick: number;
44
+ }
45
+
46
+ export interface MessageContext {
47
+ sendMessage: (message: ServerGraphRunnerMessage) => void;
48
+ sendError: (
49
+ code: string,
50
+ message: string,
51
+ details?: Record<string, unknown>
52
+ ) => void;
53
+ }
54
+
55
+ /**
56
+ * Create a logger that forwards messages through a callback
57
+ */
58
+ export function createForwardingLogger(
59
+ runId: string,
60
+ graphId: string,
61
+ onLog: (message: ServerGraphRunnerMessage) => void
62
+ ): ILogger {
63
+ const baseLogger = new DefaultLogger();
64
+
65
+ return {
66
+ log: (severity: string, text: string) => {
67
+ baseLogger.log(severity as any, text);
68
+ onLog({
69
+ type: 'log',
70
+ runId,
71
+ graphId,
72
+ level: severity,
73
+ message: text
74
+ });
75
+ }
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Prepare a registry with required dependencies injected
81
+ */
82
+ export function prepareRegistryWithDependencies(
83
+ registry: IRegistry,
84
+ logger: ILogger
85
+ ): IRegistry {
86
+ // Ensure lifecycle event emitter and logger are available in registry
87
+ if (
88
+ !registry.dependencies?.ILifecycleEventEmitter ||
89
+ !registry.dependencies?.ILogger
90
+ ) {
91
+ // Create a new registry with required dependencies injected
92
+ return {
93
+ ...registry,
94
+ dependencies: {
95
+ ...registry.dependencies,
96
+ ILifecycleEventEmitter:
97
+ registry.dependencies?.ILifecycleEventEmitter ||
98
+ new ManualLifecycleEventEmitter(),
99
+ ILogger: logger
100
+ }
101
+ };
102
+ } else {
103
+ // Replace the existing logger
104
+ return {
105
+ ...registry,
106
+ dependencies: {
107
+ ...registry.dependencies,
108
+ ILogger: logger
109
+ }
110
+ };
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Parse and validate a graph
116
+ */
117
+ export function parseAndValidateGraph(
118
+ graphData: GraphJSON,
119
+ registry: IRegistry
120
+ ): { graphInstance: GraphInstance; errors: string[] } {
121
+ const graphInstance = readGraphFromJSON({
122
+ graphJson: graphData,
123
+ registry
124
+ });
125
+
126
+ const errors = validateGraph(graphInstance);
127
+ return { graphInstance, errors };
128
+ }
129
+
130
+ /**
131
+ * Generate a unique ID
132
+ */
133
+ export function generateId(prefix: string): string {
134
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
135
+ }
136
+
137
+ /**
138
+ * Handle hello message
139
+ */
140
+ export function handleHello(
141
+ message: { type: 'hello'; protocolVersion: string },
142
+ serverId: string,
143
+ ctx: MessageContext
144
+ ): void {
145
+ ctx.sendMessage({
146
+ type: 'welcome',
147
+ protocolVersion: message.protocolVersion,
148
+ serverId,
149
+ authenticated: true,
150
+ userId: 'local-user'
151
+ });
152
+ }
153
+
154
+ /**
155
+ * Handle createSession message
156
+ */
157
+ export function handleCreateSession(
158
+ _message: { type: 'createSession'; metadata?: Record<string, unknown> },
159
+ ctx: MessageContext
160
+ ): string {
161
+ const sessionId = generateId('session');
162
+ ctx.sendMessage({
163
+ type: 'sessionCreated',
164
+ sessionId,
165
+ expiresAt: Date.now() + 24 * 60 * 60 * 1000
166
+ });
167
+ return sessionId;
168
+ }
169
+
170
+ /**
171
+ * Handle getCapabilities message
172
+ */
173
+ export function handleGetCapabilities(ctx: MessageContext): void {
174
+ const capabilities: GraphRunnerCapabilities = {
175
+ trace: true,
176
+ validation: true,
177
+ graphRegistry: false,
178
+ eventFiltering: false,
179
+ batchOperations: false,
180
+ runHistory: false,
181
+ runtimeMetadata: true,
182
+ maxConcurrentRuns: 10
183
+ };
184
+
185
+ ctx.sendMessage({
186
+ type: 'capabilities',
187
+ capabilities
188
+ });
189
+ }
190
+
191
+ /**
192
+ * Setup tracing for a run
193
+ */
194
+ export function setupTracing(
195
+ run: ActiveRun,
196
+ graphId: string,
197
+ ctx: MessageContext
198
+ ): void {
199
+ run.engine.onNodeExecutionStart.addListener((node) => {
200
+ run.performance.nodesExecuted++;
201
+ ctx.sendMessage({
202
+ type: 'trace',
203
+ runId: run.runId,
204
+ graphId,
205
+ nodeId: node.id,
206
+ event: 'start',
207
+ data: { typeName: node.description.typeName },
208
+ timestamp: Date.now() - run.startedAt
209
+ });
210
+ });
211
+
212
+ run.engine.onNodeExecutionEnd.addListener((node) => {
213
+ ctx.sendMessage({
214
+ type: 'trace',
215
+ runId: run.runId,
216
+ graphId,
217
+ nodeId: node.id,
218
+ event: 'end',
219
+ data: { typeName: node.description.typeName },
220
+ timestamp: Date.now() - run.startedAt
221
+ });
222
+ });
223
+ }
224
+
225
+ /**
226
+ * Setup variable change tracking for a run
227
+ */
228
+ export function setupVariableChangeTracking(
229
+ run: ActiveRun,
230
+ graphId: string,
231
+ ctx: MessageContext
232
+ ): void {
233
+ // Track old values so we can report them in the change event
234
+ const variableOldValues = new Map<string, unknown>();
235
+
236
+ const variables = Object.values(run.graphInstance.variables);
237
+
238
+ for (const variable of variables) {
239
+ // Store initial value as the "old" value
240
+ variableOldValues.set(variable.id, variable.get());
241
+
242
+ variable.onChanged.addListener(() => {
243
+ run.performance.variableChanges++;
244
+ const oldValue = variableOldValues.get(variable.id);
245
+ const newValue = variable.get();
246
+
247
+ // Update tracked value for next change
248
+ variableOldValues.set(variable.id, newValue);
249
+
250
+ ctx.sendMessage({
251
+ type: 'variableChanged',
252
+ runId: run.runId,
253
+ graphId,
254
+ variableName: variable.name,
255
+ oldValue,
256
+ newValue
257
+ });
258
+ });
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Execute a graph through its lifecycle phases
264
+ */
265
+ export async function executeGraphLifecycle(
266
+ run: ActiveRun,
267
+ graphId: string,
268
+ ctx: MessageContext,
269
+ options?: {
270
+ tickInterval?: number;
271
+ onStepComplete?: () => Promise<void>;
272
+ autoEnd?: boolean;
273
+ }
274
+ ): Promise<void> {
275
+ try {
276
+ const eventEmitter = run.registry.dependencies?.ILifecycleEventEmitter as
277
+ | ILifecycleEventEmitter
278
+ | undefined;
279
+
280
+ // Execute start event
281
+ if (run.executionPhase === 'start') {
282
+ if (
283
+ eventEmitter?.startEvent &&
284
+ eventEmitter.startEvent.listenerCount > 0
285
+ ) {
286
+ eventEmitter.startEvent.emit();
287
+ await run.engine.executeAllAsync();
288
+ }
289
+ run.executionPhase = 'tick';
290
+ }
291
+
292
+ // Execute tick events
293
+ if (run.executionPhase === 'tick') {
294
+ if (eventEmitter?.tickEvent && eventEmitter.tickEvent.listenerCount > 0) {
295
+ while (!run.isPaused && run.status === 'running') {
296
+ eventEmitter.tickEvent.emit();
297
+ await run.engine.executeAllAsync();
298
+ run.currentTick++;
299
+
300
+ if (options?.onStepComplete) {
301
+ await options.onStepComplete();
302
+ }
303
+
304
+ if (run.isPaused || run.status !== 'running') {
305
+ return;
306
+ }
307
+
308
+ await sleep((options?.tickInterval ?? 50) / 1000);
309
+ }
310
+ } else {
311
+ run.executionPhase = 'end';
312
+ }
313
+ }
314
+
315
+ // Execute end event
316
+ if (run.executionPhase === 'end' && !run.isPaused) {
317
+ if (eventEmitter?.endEvent && eventEmitter.endEvent.listenerCount > 0) {
318
+ eventEmitter.endEvent.emit();
319
+ await run.engine.executeAllAsync();
320
+ }
321
+ run.executionPhase = 'completed';
322
+ }
323
+
324
+ // Complete if not paused
325
+ if (!run.isPaused && !options?.autoEnd) {
326
+ run.status = 'completed';
327
+ const elapsedMs = Date.now() - run.startedAt;
328
+
329
+ ctx.sendMessage({
330
+ type: 'completed',
331
+ runId: run.runId,
332
+ graphId,
333
+ completedAt: Date.now(),
334
+ elapsedMs,
335
+ result: null,
336
+ performance: run.performance
337
+ });
338
+
339
+ run.engine.dispose();
340
+ }
341
+ } catch (error) {
342
+ run.status = 'error';
343
+ const errorMessage = error instanceof Error ? error.message : String(error);
344
+ ctx.sendError('NODE_EXECUTION_ERROR', errorMessage, {
345
+ runId: run.runId,
346
+ graphId
347
+ });
348
+ run.engine.dispose();
349
+ throw error;
350
+ }
351
+ }
352
+
353
+ /**
354
+ * Handle getServerVariables message
355
+ */
356
+ export function handleGetServerVariables(
357
+ variables: ServerVariable[],
358
+ ctx: MessageContext
359
+ ): void {
360
+ ctx.sendMessage({
361
+ type: 'serverVariables',
362
+ variables
363
+ });
364
+ }
365
+
366
+ /**
367
+ * Handle getServerEvents message
368
+ */
369
+ export function handleGetServerEvents(
370
+ events: ServerEvent[],
371
+ ctx: MessageContext
372
+ ): void {
373
+ ctx.sendMessage({
374
+ type: 'serverEvents',
375
+ events
376
+ });
377
+ }
378
+
379
+ /**
380
+ * Handle getSocketConstraints message
381
+ */
382
+ export function handleGetSocketConstraints(
383
+ message: {
384
+ nodeType: string;
385
+ socketName: string;
386
+ },
387
+ registry: IRegistry,
388
+ ctx: MessageContext
389
+ ): void {
390
+ const nodeSpec = writeNodeSpecsToJSON(registry).find(
391
+ (spec) => spec.type === message.nodeType
392
+ );
393
+ if (!nodeSpec) {
394
+ ctx.sendError('INVALID_GRAPH', `Node type not found: ${message.nodeType}`);
395
+ return;
396
+ }
397
+
398
+ const socket = [...nodeSpec.inputs, ...nodeSpec.outputs].find(
399
+ (s) => s.name === message.socketName
400
+ );
401
+
402
+ if (!socket) {
403
+ ctx.sendError(
404
+ 'INVALID_GRAPH',
405
+ `Socket not found: ${message.socketName} on node ${message.nodeType}`
406
+ );
407
+ return;
408
+ }
409
+
410
+ const constraints: {
411
+ type: 'enum' | 'range' | 'pattern' | 'custom';
412
+ choices?: Array<{ value: unknown; label: string }>;
413
+ min?: number;
414
+ max?: number;
415
+ pattern?: string;
416
+ validator?: string;
417
+ } = {
418
+ type: 'custom'
419
+ };
420
+
421
+ if ('choices' in socket && Array.isArray(socket.choices)) {
422
+ constraints.type = 'enum';
423
+ constraints.choices = socket.choices.map(
424
+ (choice: { text: string; value: unknown } | string) =>
425
+ typeof choice === 'string'
426
+ ? { value: choice, label: choice }
427
+ : { value: choice.value, label: choice.text }
428
+ );
429
+ }
430
+
431
+ ctx.sendMessage({
432
+ type: 'socketConstraints',
433
+ nodeType: message.nodeType,
434
+ socketName: message.socketName,
435
+ valueType: socket.valueType,
436
+ constraints
437
+ });
438
+ }
439
+
440
+ /**
441
+ * Handle getNodeTypes message
442
+ */
443
+ export function handleGetNodeTypes(
444
+ registry: IRegistry,
445
+ ctx: MessageContext
446
+ ): void {
447
+ let nodes: NodeSpecJSON[] = [];
448
+
449
+ if (registry.nodes) {
450
+ nodes = writeNodeSpecsToJSON(registry);
451
+ }
452
+
453
+ ctx.sendMessage({
454
+ type: 'nodeTypes',
455
+ nodes
456
+ });
457
+ }
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Local graph runner plugin for in-browser graph execution
3
+ * Uses the local Engine instead of a remote server
4
+ */
5
+
6
+ import type { System } from '../../system/system.js';
7
+ import { plugin } from '../../system/plugin.js';
8
+ import type { IRegistry } from '@kiberon-labs/behave-graph';
9
+ import { GraphRunnerClient } from '../graphrunner/client.js';
10
+ import { LocalTransport } from './transport.js';
11
+ import {
12
+ graphRunnerClientPlugin,
13
+ type ServerEvent,
14
+ type ServerVariable,
15
+ type SessionFactory
16
+ } from '../graphrunner/index.js';
17
+ import { DefaultSessionFactory } from '../graphrunner/session.js';
18
+ import { localGraphRunnerStoreFactory } from './store.js';
19
+ import { LocalGraphRunnerPanel } from './panel.js';
20
+ import { MenuItemElement } from '../../components/menubar/menuItem.js';
21
+ import { ErrorBoundary } from 'react-error-boundary';
22
+
23
+ export * from './transport.js';
24
+ export * from './store.js';
25
+ export * from './panel.js';
26
+ export * from './types.js';
27
+
28
+ /**
29
+ * Options for the local graph runner plugin
30
+ */
31
+ export interface LocalGraphRunnerPluginOptions {
32
+ /**
33
+ * Node registry with registered nodes, values, and dependencies.
34
+ * Required for graph execution.
35
+ */
36
+ registry: IRegistry;
37
+ variables?: ServerVariable[];
38
+ events?: ServerEvent[];
39
+ sessionFactory?: SessionFactory;
40
+
41
+ /**
42
+ * Custom tick strategy hook for controlling timing between tick events.
43
+ * If not provided, defaults to requestAnimationFrame for smooth browser refresh sync.
44
+ */
45
+ tickStrategy?: () => Promise<void>;
46
+
47
+ /**
48
+ * Whether to skip automatic connection.
49
+ * Default: false (will attempt to connect immediately)
50
+ */
51
+ skipAutoConnect?: boolean;
52
+
53
+ /**
54
+ * Whether to add the menu item to the Window menu.
55
+ * Default: true
56
+ */
57
+ addMenuItem?: boolean;
58
+ }
59
+
60
+ /**
61
+ * Default RAF-based tick strategy for smooth browser animation frame sync
62
+ */
63
+ const defaultRafTickStrategy = (): Promise<void> => {
64
+ return new Promise((resolve) => {
65
+ requestAnimationFrame(() => resolve());
66
+ });
67
+ };
68
+
69
+ /**
70
+ * Plugin initialization function for local graph execution
71
+ * Registers a GraphRunnerClient with a local transport that executes graphs in-browser
72
+ */
73
+ export async function localGraphRunnerPluginLoader(
74
+ system: System,
75
+ options: LocalGraphRunnerPluginOptions
76
+ ): Promise<void> {
77
+ // Create local graph runner store
78
+ const localStore = localGraphRunnerStoreFactory();
79
+ system.decorate('localGraphRunnerStore', localStore);
80
+
81
+ // Create or use provided session factory with tick strategy
82
+ const tickStrategy = options.tickStrategy ?? defaultRafTickStrategy;
83
+ const sessionFactory =
84
+ options.sessionFactory ??
85
+ new DefaultSessionFactory({
86
+ tickStrategy
87
+ });
88
+
89
+ // Create local transport with access to the node registry and store
90
+ const transport = new LocalTransport(options.registry, {
91
+ ...options,
92
+ store: localStore,
93
+ sessionFactory
94
+ });
95
+
96
+ // Create client with the local transport and message activity tracking
97
+ const client = new GraphRunnerClient({
98
+ transport,
99
+ protocolVersion: '1.0.0',
100
+ auth: { type: 'none' },
101
+ onMessageActivity: (direction, message) => {
102
+ // Access the store from the system after it's registered
103
+ const graphRunnerStore = system.runner.store;
104
+ if (graphRunnerStore) {
105
+ graphRunnerStore.getState().addMessageActivity(direction, message);
106
+ }
107
+ }
108
+ });
109
+
110
+ // Register the graph runner client plugin
111
+ // This will create the graphRunnerClientStore and decorate it on the system
112
+ system.registerPlugin(graphRunnerClientPlugin, {
113
+ client
114
+ });
115
+
116
+ // Register the local graph runner panel
117
+ system.tabLoader.register('localGraphRunner', () => {
118
+ return {
119
+ id: 'localGraphRunner',
120
+ closable: true,
121
+ title: 'Local Graph Runner',
122
+ group: 'default',
123
+ content: () => (
124
+ <ErrorBoundary
125
+ fallback={<div>Error loading Local Graph Runner panel</div>}
126
+ >
127
+ <LocalGraphRunnerPanel />
128
+ </ErrorBoundary>
129
+ )
130
+ };
131
+ });
132
+
133
+ // Add menu item to Window menu (unless disabled)
134
+ if (options.addMenuItem !== false) {
135
+ const menuStore = system.menubarStore;
136
+ const currentItems = menuStore.getState().items;
137
+ const windowMenu = currentItems.find((menu) => menu.name === 'window');
138
+
139
+ if (windowMenu) {
140
+ // Add the Local Graph Runner menu item to the Window menu
141
+ const newMenuItem = {
142
+ name: 'localGraphRunner',
143
+ render: function LocalGraphRunnerMenuItem() {
144
+ return (
145
+ <MenuItemElement
146
+ onClick={() =>
147
+ system.tabStore.getState().openTab('localGraphRunner')
148
+ }
149
+ key="localGraphRunner"
150
+ >
151
+ Local Graph Runner
152
+ </MenuItemElement>
153
+ );
154
+ }
155
+ };
156
+
157
+ menuStore
158
+ .getState()
159
+ .setSubMenuItems('window', [...windowMenu.items, newMenuItem]);
160
+ }
161
+ }
162
+ }
163
+
164
+ export const localGraphRunnerPlugin = plugin(localGraphRunnerPluginLoader, {
165
+ name: 'graphrunner-local'
166
+ });