@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,704 @@
1
+ /**
2
+ * Graph Runner client implementation with abstracted transport layer
3
+ */
4
+
5
+ import type {
6
+ EventFilter,
7
+ GraphRunnerCapabilities,
8
+ GraphRunnerClientConfig,
9
+ GraphRunnerMessage,
10
+ ServerGraphRunnerMessage,
11
+ RunStatus,
12
+ ServerEvent,
13
+ ServerVariable,
14
+ SocketConstraint
15
+ } from './types';
16
+ import {
17
+ WebSocketTransport,
18
+ type ITransport,
19
+ type TransportState
20
+ } from './transport';
21
+ import type { NodeSpecJSON, GraphJSON } from '@kiberon-labs/behave-graph';
22
+
23
+ /**
24
+ * Extract message type by its 'type' field
25
+ */
26
+ type ExtractMessage<T extends string> = Extract<
27
+ ServerGraphRunnerMessage,
28
+ { type: T }
29
+ >;
30
+
31
+ /**
32
+ * Map of client request message types to their expected server response types
33
+ */
34
+ type RequestResponseMap = {
35
+ createSession: 'sessionCreated';
36
+ resumeSession: 'sessionResumed';
37
+ closeSession: 'sessionClosed';
38
+ getCapabilities: 'capabilities';
39
+ batchGetStatus: 'batchStatus';
40
+ getRunHistory: 'runHistory';
41
+ getServerVariables: 'serverVariables';
42
+ getServerEvents: 'serverEvents';
43
+ getSocketConstraints: 'socketConstraints';
44
+ registerGraph: 'graphRegistered';
45
+ getNodeTypes: 'nodeTypes';
46
+ validateGraph: 'validationResult';
47
+ runGraph: 'runStarted';
48
+ stopGraph: 'stopped';
49
+ getStatus: 'status';
50
+ listGraphs: 'graphList';
51
+ };
52
+
53
+ /**
54
+ * Extract the response type for a given request type
55
+ */
56
+ type ResponseForRequest<T extends keyof RequestResponseMap> = ExtractMessage<
57
+ RequestResponseMap[T]
58
+ >;
59
+
60
+ /**
61
+ * Handler for messages received from the server
62
+ * Generic type parameter ensures the handler receives the correct message type
63
+ */
64
+ type ServerMessageHandler<T extends ServerGraphRunnerMessage['type']> = (
65
+ message: ExtractMessage<T>
66
+ ) => void;
67
+
68
+ /**
69
+ * Client for the Behave-Graph Execution Protocol
70
+ * Supports multiple transport implementations (WebSocket, HTTP, etc.)
71
+ */
72
+ export class GraphRunnerClient {
73
+ public transport: ITransport;
74
+ private config: GraphRunnerClientConfig;
75
+ private messageHandlers = new Map<string, Set<ServerMessageHandler<any>>>();
76
+ private sessionId: string | null = null;
77
+ private capabilities: GraphRunnerCapabilities | null = null;
78
+ private serverId: string | null = null;
79
+ private authenticated = false;
80
+ private userId: string | null = null;
81
+ private pendingRequests = new Map<
82
+ string,
83
+ { resolve: (value: any) => void; reject: (error: any) => void }
84
+ >();
85
+ private connectionState:
86
+ | 'disconnected'
87
+ | 'connecting'
88
+ | 'authenticating'
89
+ | 'connected' = 'disconnected';
90
+
91
+ constructor(config: GraphRunnerClientConfig) {
92
+ this.config = config;
93
+
94
+ // Use provided transport or create default WebSocket transport
95
+ if (config.transport) {
96
+ this.transport = config.transport;
97
+ } else if (config.url) {
98
+ this.transport = new WebSocketTransport({
99
+ url: config.url,
100
+ reconnectInterval: config.reconnectInterval,
101
+ heartbeatInterval: config.heartbeatInterval
102
+ });
103
+ } else {
104
+ throw new Error('Either transport or url must be provided');
105
+ }
106
+
107
+ // Set up transport handlers
108
+ this.transport.onMessage((message) =>
109
+ this.handleMessage(message as unknown as ServerGraphRunnerMessage)
110
+ );
111
+ this.transport.onStateChange((state) =>
112
+ this.handleTransportStateChange(state)
113
+ );
114
+ this.transport.onError((error) => this.handleTransportError(error));
115
+ }
116
+
117
+ async connect(): Promise<void> {
118
+ return new Promise((resolve, reject) => {
119
+ this.connectionState = 'connecting';
120
+
121
+ // Wait for transport to connect
122
+ this.transport
123
+ .connect()
124
+ .then(() => {
125
+ this.connectionState = 'authenticating';
126
+
127
+ // Wait for welcome message
128
+ const welcomeHandler = (msg: ServerGraphRunnerMessage) => {
129
+ if (msg.type === 'welcome') {
130
+ this.serverId = msg.serverId;
131
+ this.authenticated = msg.authenticated;
132
+ this.userId = msg.userId ?? null;
133
+ this.connectionState = 'connected';
134
+ this.off('welcome', welcomeHandler);
135
+ this.off('error', welcomeHandler);
136
+ resolve();
137
+ } else if (msg.type === 'error') {
138
+ this.connectionState = 'disconnected';
139
+ this.off('welcome', welcomeHandler);
140
+ this.off('error', welcomeHandler);
141
+ reject(new Error(`Connection failed: ${msg.message}`));
142
+ }
143
+ };
144
+
145
+ this.on('welcome', welcomeHandler);
146
+ this.on('error', welcomeHandler);
147
+
148
+ // Send hello message
149
+ this.send({
150
+ type: 'hello',
151
+ protocolVersion: this.config.protocolVersion ?? '1.0.0',
152
+ auth: this.config.auth ?? { type: 'none' }
153
+ });
154
+ })
155
+ .then(async () => {
156
+ await this.getCapabilities();
157
+ })
158
+ .catch((error) => {
159
+ this.connectionState = 'disconnected';
160
+ reject(error);
161
+ });
162
+ });
163
+ }
164
+
165
+ disconnect(): void {
166
+ this.transport.disconnect();
167
+ this.connectionState = 'disconnected';
168
+ }
169
+
170
+ async createSession(metadata?: Record<string, unknown>): Promise<string> {
171
+ const response = await this.request<'createSession'>({
172
+ type: 'createSession',
173
+ metadata: metadata ?? this.config.metadata
174
+ });
175
+ this.sessionId = response.sessionId;
176
+ return response.sessionId;
177
+ }
178
+
179
+ async resumeSession(sessionId: string): Promise<string[]> {
180
+ const response = await this.request<'resumeSession'>({
181
+ type: 'resumeSession',
182
+ sessionId
183
+ });
184
+ this.sessionId = sessionId;
185
+ return response.activeRuns;
186
+ }
187
+
188
+ async closeSession(): Promise<void> {
189
+ if (!this.sessionId) return;
190
+ await this.request({ type: 'closeSession', sessionId: this.sessionId });
191
+ this.sessionId = null;
192
+ }
193
+
194
+ async getCapabilities(): Promise<GraphRunnerCapabilities> {
195
+ const response = await this.request<'getCapabilities'>({
196
+ type: 'getCapabilities'
197
+ });
198
+ this.capabilities = response.capabilities;
199
+ return response.capabilities;
200
+ }
201
+
202
+ async getServerVariables(): Promise<ServerVariable[]> {
203
+ this.ensureSession();
204
+ const response = await this.request<'getServerVariables'>({
205
+ type: 'getServerVariables',
206
+ sessionId: this.sessionId!
207
+ });
208
+ return response.variables;
209
+ }
210
+
211
+ async getServerEvents(): Promise<ServerEvent[]> {
212
+ this.ensureSession();
213
+ const response = await this.request<'getServerEvents'>({
214
+ type: 'getServerEvents',
215
+ sessionId: this.sessionId!
216
+ });
217
+ return response.events;
218
+ }
219
+
220
+ async getSocketConstraints(
221
+ nodeType: string,
222
+ socketName: string
223
+ ): Promise<{
224
+ valueType: string;
225
+ constraints: SocketConstraint;
226
+ }> {
227
+ this.ensureSession();
228
+ const response = await this.request<'getSocketConstraints'>({
229
+ type: 'getSocketConstraints',
230
+ sessionId: this.sessionId!,
231
+ nodeType,
232
+ socketName
233
+ });
234
+ return {
235
+ valueType: response.valueType,
236
+ constraints: response.constraints
237
+ };
238
+ }
239
+
240
+ async getNodeTypes(): Promise<NodeSpecJSON[]> {
241
+ this.ensureSession();
242
+ const response = await this.request<'getNodeTypes'>({
243
+ type: 'getNodeTypes',
244
+ sessionId: this.sessionId!
245
+ });
246
+ return response.nodes;
247
+ }
248
+
249
+ async registerGraph(graphId: string, graph: GraphJSON): Promise<void> {
250
+ this.ensureSession();
251
+ await this.request<'registerGraph'>({
252
+ type: 'registerGraph',
253
+ sessionId: this.sessionId!,
254
+ graphId,
255
+ graph
256
+ });
257
+ }
258
+
259
+ async listGraphs(): Promise<
260
+ Array<{ graphId: string; registeredAt: number }>
261
+ > {
262
+ this.ensureSession();
263
+ const response = await this.request<'listGraphs'>({
264
+ type: 'listGraphs',
265
+ sessionId: this.sessionId!
266
+ });
267
+ return response.graphs;
268
+ }
269
+
270
+ async validateGraph(
271
+ graphId: string,
272
+ graph: unknown
273
+ ): Promise<{
274
+ valid: boolean;
275
+ errors: Array<{ nodeId: string; message: string; severity: string }>;
276
+ warnings: Array<{ nodeId: string; message: string; severity: string }>;
277
+ }> {
278
+ this.ensureSession();
279
+ const response = await this.request<'validateGraph'>({
280
+ type: 'validateGraph',
281
+ sessionId: this.sessionId!,
282
+ graphId,
283
+ graph
284
+ });
285
+ return {
286
+ valid: response.valid,
287
+ errors: response.errors,
288
+ warnings: response.warnings
289
+ };
290
+ }
291
+
292
+ async runGraph(
293
+ graphId: string,
294
+ options?: { graph?: unknown; inputs?: unknown; trace?: boolean }
295
+ ): Promise<string> {
296
+ this.ensureSession();
297
+ const response = await this.request<'runGraph'>({
298
+ type: 'runGraph',
299
+ sessionId: this.sessionId!,
300
+ graphId,
301
+ graph: options?.graph as GraphJSON,
302
+ inputs: options?.inputs,
303
+ options: {
304
+ trace: options?.trace ?? true
305
+ }
306
+ });
307
+
308
+ return response.runId;
309
+ }
310
+
311
+ async stopGraph(runId: string): Promise<void> {
312
+ this.ensureSession();
313
+ await this.request({
314
+ type: 'stopGraph',
315
+ sessionId: this.sessionId!,
316
+ runId
317
+ });
318
+ }
319
+
320
+ async getStatus(runId: string) {
321
+ this.ensureSession();
322
+ const response = await this.request({
323
+ type: 'getStatus',
324
+ sessionId: this.sessionId!,
325
+ runId
326
+ });
327
+ return response;
328
+ }
329
+
330
+ async batchGetStatus(
331
+ runIds: string[]
332
+ ): Promise<Array<{ runId: string; status: RunStatus; elapsedMs: number }>> {
333
+ this.ensureSession();
334
+ const response = await this.request({
335
+ type: 'batchGetStatus',
336
+ sessionId: this.sessionId!,
337
+ runIds
338
+ });
339
+ return response.statuses;
340
+ }
341
+
342
+ async subscribe(runId: string, filter?: EventFilter): Promise<void> {
343
+ this.ensureSession();
344
+ this.send({
345
+ type: 'subscribe',
346
+ sessionId: this.sessionId!,
347
+ runId,
348
+ filter
349
+ });
350
+ }
351
+
352
+ async unsubscribe(runId: string): Promise<void> {
353
+ this.ensureSession();
354
+ this.send({
355
+ type: 'unsubscribe',
356
+ sessionId: this.sessionId!,
357
+ runId
358
+ });
359
+ }
360
+
361
+ /**
362
+ * Send a nodeAdded event
363
+ * Used to notify the server when a node is added to the graph during execution
364
+ */
365
+ sendNodeAdded(
366
+ runId: string,
367
+ graphId: string,
368
+ nodeId: string,
369
+ nodeType: string,
370
+ nodeData?: unknown
371
+ ): void {
372
+ this.send({
373
+ type: 'nodeAdded',
374
+ runId,
375
+ graphId,
376
+ nodeId,
377
+ nodeType,
378
+ nodeData
379
+ });
380
+ }
381
+
382
+ async getRunHistory(options?: { limit?: number; graphId?: string }): Promise<
383
+ Array<{
384
+ runId: string;
385
+ graphId: string;
386
+ status: RunStatus;
387
+ startedAt: number;
388
+ completedAt?: number;
389
+ elapsedMs: number;
390
+ result?: unknown;
391
+ }>
392
+ > {
393
+ this.ensureSession();
394
+ const response = await this.request({
395
+ type: 'getRunHistory',
396
+ sessionId: this.sessionId!,
397
+ limit: options?.limit,
398
+ graphId: options?.graphId
399
+ });
400
+ return response.runs;
401
+ }
402
+
403
+ // Realtime modification methods
404
+
405
+ addNode(
406
+ runId: string,
407
+ nodeId: string,
408
+ nodeType: string,
409
+ nodeData?: Record<string, unknown>,
410
+ position?: { x: number; y: number }
411
+ ): void {
412
+ this.ensureSession();
413
+ this.send({
414
+ type: 'addNode',
415
+ sessionId: this.sessionId!,
416
+ runId,
417
+ nodeId,
418
+ nodeType,
419
+ nodeData,
420
+ position
421
+ });
422
+ }
423
+
424
+ removeNode(runId: string, nodeId: string): void {
425
+ this.ensureSession();
426
+ this.send({
427
+ type: 'removeNode',
428
+ sessionId: this.sessionId!,
429
+ runId,
430
+ nodeId
431
+ });
432
+ }
433
+
434
+ updateSocketValue(
435
+ runId: string,
436
+ nodeId: string,
437
+ socketName: string,
438
+ value: unknown
439
+ ): void {
440
+ this.ensureSession();
441
+ this.send({
442
+ type: 'updateSocketValue',
443
+ sessionId: this.sessionId!,
444
+ runId,
445
+ nodeId,
446
+ socketName,
447
+ value
448
+ });
449
+ }
450
+
451
+ updateNodeParam(
452
+ runId: string,
453
+ nodeId: string,
454
+ paramName: string,
455
+ value: unknown
456
+ ): void {
457
+ this.ensureSession();
458
+ this.send({
459
+ type: 'updateNodeParam',
460
+ sessionId: this.sessionId!,
461
+ runId,
462
+ nodeId,
463
+ paramName,
464
+ value
465
+ });
466
+ }
467
+
468
+ createLink(
469
+ runId: string,
470
+ fromNodeId: string,
471
+ fromSocket: string,
472
+ toNodeId: string,
473
+ toSocket: string
474
+ ): void {
475
+ this.ensureSession();
476
+ this.send({
477
+ type: 'createLink',
478
+ sessionId: this.sessionId!,
479
+ runId,
480
+ fromNodeId,
481
+ fromSocket,
482
+ toNodeId,
483
+ toSocket
484
+ });
485
+ }
486
+
487
+ removeLink(
488
+ runId: string,
489
+ fromNodeId: string,
490
+ fromSocket: string,
491
+ toNodeId: string,
492
+ toSocket: string
493
+ ): void {
494
+ this.ensureSession();
495
+ this.send({
496
+ type: 'removeLink',
497
+ sessionId: this.sessionId!,
498
+ runId,
499
+ fromNodeId,
500
+ fromSocket,
501
+ toNodeId,
502
+ toSocket
503
+ });
504
+ }
505
+
506
+ directExecuteNode(
507
+ runId: string,
508
+ nodeId: string,
509
+ inputSocketName: string,
510
+ inputValue: unknown,
511
+ autoExecMode: 'new' | 'current' = 'current'
512
+ ): void {
513
+ this.ensureSession();
514
+ this.send({
515
+ type: 'directExecuteNode',
516
+ sessionId: this.sessionId!,
517
+ runId,
518
+ nodeId,
519
+ inputSocketName,
520
+ inputValue,
521
+ autoExecMode
522
+ });
523
+ }
524
+
525
+ on<T extends ServerGraphRunnerMessage['type']>(
526
+ messageType: T,
527
+ handler: ServerMessageHandler<T>
528
+ ): void {
529
+ if (!this.messageHandlers.has(messageType)) {
530
+ this.messageHandlers.set(messageType, new Set());
531
+ }
532
+ this.messageHandlers.get(messageType)!.add(handler);
533
+ }
534
+
535
+ off<T extends ServerGraphRunnerMessage['type']>(
536
+ messageType: T,
537
+ handler: ServerMessageHandler<T>
538
+ ): void {
539
+ const handlers = this.messageHandlers.get(messageType);
540
+ if (handlers) {
541
+ handlers.delete(handler);
542
+ }
543
+ }
544
+
545
+ getConnectionState():
546
+ | 'disconnected'
547
+ | 'connecting'
548
+ | 'authenticating'
549
+ | 'connected' {
550
+ return this.connectionState;
551
+ }
552
+
553
+ getSessionId(): string | null {
554
+ return this.sessionId;
555
+ }
556
+
557
+ getCachedCapabilities(): GraphRunnerCapabilities | null {
558
+ return this.capabilities;
559
+ }
560
+
561
+ isAuthenticated(): boolean {
562
+ return this.authenticated;
563
+ }
564
+
565
+ getUserId(): string | null {
566
+ return this.userId;
567
+ }
568
+
569
+ getServerId(): string | null {
570
+ return this.serverId;
571
+ }
572
+
573
+ isConnected(): boolean {
574
+ return this.connectionState === 'connected';
575
+ }
576
+
577
+ private send(message: GraphRunnerMessage): void {
578
+ this.transport.send(message);
579
+ // Track sent message if callback is configured
580
+ if (this.config.onMessageActivity) {
581
+ this.config.onMessageActivity('sent', message);
582
+ }
583
+ }
584
+
585
+ private handleMessage(message: ServerGraphRunnerMessage): void {
586
+ // Track received message if callback is configured
587
+ if (this.config.onMessageActivity) {
588
+ this.config.onMessageActivity(
589
+ 'received',
590
+ message as unknown as GraphRunnerMessage
591
+ );
592
+ }
593
+
594
+ // Notify all handlers for this message type
595
+ const handlers = this.messageHandlers.get(message.type);
596
+ if (handlers) {
597
+ handlers.forEach((handler) => handler(message));
598
+ }
599
+
600
+ // Also notify wildcard handlers
601
+ const wildcardHandlers = this.messageHandlers.get('*');
602
+ if (wildcardHandlers) {
603
+ wildcardHandlers.forEach((handler) => handler(message));
604
+ }
605
+ }
606
+
607
+ private handleTransportStateChange(state: TransportState): void {
608
+ // Map transport state to connection state
609
+ if (state === 'disconnected') {
610
+ this.connectionState = 'disconnected';
611
+ } else if (state === 'connecting') {
612
+ this.connectionState = 'connecting';
613
+ }
614
+ }
615
+
616
+ private handleTransportError(error: Error): void {
617
+ console.error('Transport error:', error);
618
+ // Reject all pending requests
619
+ this.pendingRequests.forEach(({ reject }) => {
620
+ reject(error);
621
+ });
622
+ this.pendingRequests.clear();
623
+ }
624
+
625
+ private async request<T extends keyof RequestResponseMap>(
626
+ message: Extract<GraphRunnerMessage, { type: T }>
627
+ ): Promise<ResponseForRequest<T>> {
628
+ return new Promise((resolve, reject) => {
629
+ const requestId = this.generateRequestId();
630
+ this.pendingRequests.set(requestId, { resolve, reject });
631
+
632
+ const timeout = setTimeout(() => {
633
+ this.pendingRequests.delete(requestId);
634
+ reject(new Error('Request timeout'));
635
+ }, 30000);
636
+
637
+ const responseType = this.getExpectedResponseType(
638
+ (message as { type: T }).type
639
+ );
640
+
641
+ const handler = (msg: ExtractMessage<typeof responseType>) => {
642
+ clearTimeout(timeout);
643
+ this.pendingRequests.delete(requestId);
644
+ this.off(responseType, handler);
645
+ resolve(msg as ResponseForRequest<T>);
646
+ };
647
+
648
+ const errorHandler = (msg: ExtractMessage<'error'>) => {
649
+ clearTimeout(timeout);
650
+ this.pendingRequests.delete(requestId);
651
+ this.off(responseType, handler);
652
+ this.off('error', errorHandler);
653
+ reject(new Error(msg.message));
654
+ };
655
+
656
+ this.on(responseType, handler);
657
+ this.on('error', errorHandler);
658
+
659
+ try {
660
+ this.send(message);
661
+ } catch (error) {
662
+ clearTimeout(timeout);
663
+ this.pendingRequests.delete(requestId);
664
+ this.off(responseType, handler);
665
+ this.off('error', errorHandler);
666
+ reject(error);
667
+ }
668
+ });
669
+ }
670
+
671
+ private ensureSession(): void {
672
+ if (!this.sessionId) {
673
+ throw new Error('No active session. Call createSession() first.');
674
+ }
675
+ }
676
+
677
+ private generateRequestId(): string {
678
+ return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
679
+ }
680
+
681
+ private getExpectedResponseType<T extends keyof RequestResponseMap>(
682
+ requestType: T
683
+ ): RequestResponseMap[T] {
684
+ const responseMap: RequestResponseMap = {
685
+ createSession: 'sessionCreated',
686
+ resumeSession: 'sessionResumed',
687
+ closeSession: 'sessionClosed',
688
+ getCapabilities: 'capabilities',
689
+ getServerVariables: 'serverVariables',
690
+ getServerEvents: 'serverEvents',
691
+ getSocketConstraints: 'socketConstraints',
692
+ getNodeTypes: 'nodeTypes',
693
+ registerGraph: 'graphRegistered',
694
+ listGraphs: 'graphList',
695
+ validateGraph: 'validationResult',
696
+ runGraph: 'runStarted',
697
+ stopGraph: 'stopped',
698
+ getStatus: 'status',
699
+ batchGetStatus: 'batchStatus',
700
+ getRunHistory: 'runHistory'
701
+ };
702
+ return responseMap[requestType];
703
+ }
704
+ }