@motiadev/workbench 0.0.6 → 0.0.7-build.20250529212805

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 (295) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -40
  3. package/dist/README.md +105 -0
  4. package/dist/index.d.ts +1 -2
  5. package/dist/index.html +31 -0
  6. package/dist/index.js +0 -1
  7. package/dist/middleware.d.ts +2 -2
  8. package/dist/middleware.js +10 -17
  9. package/dist/postcss.config.mjs +9 -0
  10. package/dist/src/components/app-sidebar.d.ts +0 -1
  11. package/dist/src/components/app-sidebar.js +15 -8
  12. package/dist/src/components/endpoints/endpoint-badge.d.ts +10 -0
  13. package/dist/src/components/endpoints/endpoint-badge.js +22 -0
  14. package/dist/src/components/endpoints/endpoint-call.d.ts +8 -0
  15. package/dist/src/components/endpoints/endpoint-call.js +57 -0
  16. package/dist/src/components/endpoints/endpoints.d.ts +1 -0
  17. package/dist/src/components/endpoints/endpoints.js +34 -0
  18. package/dist/src/components/endpoints/hooks/use-get-endpoints.d.ts +15 -0
  19. package/dist/src/components/endpoints/hooks/use-get-endpoints.js +8 -0
  20. package/dist/src/components/endpoints/hooks/use-json-schema-to-json.d.ts +4 -0
  21. package/dist/src/components/endpoints/hooks/use-json-schema-to-json.js +11 -0
  22. package/dist/src/components/endpoints/hooks/use-path-params.d.ts +1 -0
  23. package/dist/src/components/endpoints/hooks/use-path-params.js +4 -0
  24. package/dist/src/components/endpoints/hooks/use-state-stream.d.ts +4 -0
  25. package/dist/src/components/endpoints/hooks/use-state-stream.js +8 -0
  26. package/dist/src/components/endpoints/hooks/utils.d.ts +1 -0
  27. package/dist/src/components/endpoints/hooks/utils.js +29 -0
  28. package/dist/src/components/endpoints/response-body.d.ts +7 -0
  29. package/dist/src/components/endpoints/response-body.js +6 -0
  30. package/dist/src/components/endpoints/selected-endpoint.d.ts +7 -0
  31. package/dist/src/components/endpoints/selected-endpoint.js +7 -0
  32. package/dist/src/components/{log-console.d.ts → logs/log-console.d.ts} +0 -1
  33. package/dist/src/components/logs/log-console.js +69 -0
  34. package/dist/src/components/logs/log-detail.d.ts +8 -0
  35. package/dist/src/components/logs/log-detail.js +15 -0
  36. package/dist/src/components/logs/log-field.d.ts +8 -0
  37. package/dist/src/components/logs/log-field.js +20 -0
  38. package/dist/src/components/{log-level-badge.d.ts → logs/log-level-badge.d.ts} +2 -1
  39. package/dist/src/components/{log-level-badge.js → logs/log-level-badge.js} +2 -2
  40. package/dist/src/components/logs/log-level-dot.d.ts +4 -0
  41. package/dist/src/components/logs/log-level-dot.js +17 -0
  42. package/dist/src/components/logs/logs.d.ts +1 -0
  43. package/dist/src/components/logs/logs.js +18 -0
  44. package/dist/src/components/root-motia.d.ts +2 -0
  45. package/dist/src/components/root-motia.js +5 -0
  46. package/dist/src/components/states/hooks/states-hooks.d.ts +3 -0
  47. package/dist/src/components/states/hooks/states-hooks.js +36 -0
  48. package/dist/src/components/states/state-detail.d.ts +7 -0
  49. package/dist/src/components/states/state-detail.js +12 -0
  50. package/dist/src/components/states/state-value.d.ts +8 -0
  51. package/dist/src/components/states/state-value.js +51 -0
  52. package/dist/src/components/states/states.d.ts +1 -0
  53. package/dist/src/components/states/states.js +21 -0
  54. package/dist/src/components/ui/badge.d.ts +3 -4
  55. package/dist/src/components/ui/badge.js +4 -4
  56. package/dist/src/components/ui/button.d.ts +2 -3
  57. package/dist/src/components/ui/button.js +2 -3
  58. package/dist/src/components/ui/collapsible.d.ts +1 -2
  59. package/dist/src/components/ui/collapsible.js +1 -1
  60. package/dist/src/components/ui/dialog.d.ts +2 -3
  61. package/dist/src/components/ui/dialog.js +12 -12
  62. package/dist/src/components/ui/dropdown-menu.d.ts +25 -0
  63. package/dist/src/components/ui/dropdown-menu.js +50 -0
  64. package/dist/src/components/ui/input.d.ts +0 -1
  65. package/dist/src/components/ui/label.d.ts +3 -4
  66. package/dist/src/components/ui/label.js +6 -6
  67. package/dist/src/components/ui/select.d.ts +2 -3
  68. package/dist/src/components/ui/select.js +14 -15
  69. package/dist/src/components/ui/separator.d.ts +0 -1
  70. package/dist/src/components/ui/sheet.d.ts +1 -2
  71. package/dist/src/components/ui/sheet.js +2 -2
  72. package/dist/src/components/ui/sidebar.d.ts +12 -67
  73. package/dist/src/components/ui/sidebar.js +14 -219
  74. package/dist/src/components/ui/skeleton.d.ts +1 -1
  75. package/dist/src/components/ui/switch.d.ts +2 -3
  76. package/dist/src/components/ui/switch.js +4 -4
  77. package/dist/src/components/ui/table.d.ts +0 -1
  78. package/dist/src/components/ui/table.js +1 -1
  79. package/dist/src/components/ui/textarea.d.ts +1 -2
  80. package/dist/src/components/ui/textarea.js +4 -4
  81. package/dist/src/components/ui/theme-toggle.d.ts +2 -0
  82. package/dist/src/components/ui/theme-toggle.js +11 -0
  83. package/dist/src/components/ui/tooltip.d.ts +0 -1
  84. package/dist/src/hooks/use-debounced.d.ts +1 -0
  85. package/dist/src/hooks/use-debounced.js +18 -0
  86. package/dist/src/hooks/use-list-flows.d.ts +0 -2
  87. package/dist/src/hooks/use-list-flows.js +6 -10
  88. package/dist/src/hooks/use-log-listener.d.ts +1 -6
  89. package/dist/src/hooks/use-log-listener.js +3 -18
  90. package/dist/src/hooks/use-mobile.d.ts +0 -1
  91. package/dist/src/hooks/use-theme.d.ts +6 -0
  92. package/dist/src/hooks/use-theme.js +28 -0
  93. package/dist/src/index.css +169 -0
  94. package/dist/src/lib/utils.d.ts +0 -1
  95. package/dist/src/main.d.ts +0 -8
  96. package/dist/src/main.js +11 -7
  97. package/dist/src/publicComponents/api-node.d.ts +2 -5
  98. package/dist/src/publicComponents/api-node.js +3 -3
  99. package/dist/src/publicComponents/base-handle.d.ts +1 -1
  100. package/dist/src/publicComponents/base-handle.js +5 -2
  101. package/dist/src/publicComponents/base-node.d.ts +5 -5
  102. package/dist/src/publicComponents/base-node.js +13 -8
  103. package/dist/src/publicComponents/colorMap.d.ts +6 -0
  104. package/dist/src/publicComponents/colorMap.js +6 -0
  105. package/dist/src/publicComponents/components/header-bar.d.ts +11 -0
  106. package/dist/src/publicComponents/components/header-bar.js +15 -0
  107. package/dist/src/publicComponents/cron-node.d.ts +2 -0
  108. package/dist/src/publicComponents/cron-node.js +7 -0
  109. package/dist/src/publicComponents/emits.d.ts +1 -1
  110. package/dist/src/publicComponents/emits.js +2 -2
  111. package/dist/src/publicComponents/event-node.d.ts +0 -2
  112. package/dist/src/publicComponents/event-node.js +3 -5
  113. package/dist/src/publicComponents/node-details.d.ts +17 -0
  114. package/dist/src/publicComponents/node-details.js +19 -0
  115. package/dist/src/publicComponents/node-props.d.ts +5 -3
  116. package/dist/src/publicComponents/noop-node.d.ts +0 -1
  117. package/dist/src/publicComponents/noop-node.js +1 -1
  118. package/dist/src/publicComponents/subscribe.d.ts +2 -3
  119. package/dist/src/publicComponents/subscribe.js +2 -2
  120. package/dist/src/route-wrapper.d.ts +2 -4
  121. package/dist/src/route-wrapper.js +2 -2
  122. package/dist/src/routes/endpoints-page.d.ts +1 -0
  123. package/dist/src/routes/endpoints-page.js +5 -0
  124. package/dist/src/routes/flow.d.ts +1 -0
  125. package/dist/src/routes/flow.js +22 -0
  126. package/dist/src/routes/index.d.ts +1 -2
  127. package/dist/src/routes/index.js +5 -8
  128. package/dist/src/routes/logs-page.d.ts +1 -0
  129. package/dist/src/routes/logs-page.js +12 -0
  130. package/dist/src/routes/states-page.d.ts +1 -0
  131. package/dist/src/routes/states-page.js +5 -0
  132. package/dist/src/stores/use-logs.d.ts +3 -2
  133. package/dist/src/stores/use-logs.js +52 -6
  134. package/dist/src/views/flow/arrow-head.d.ts +2 -2
  135. package/dist/src/views/flow/arrow-head.js +5 -1
  136. package/dist/src/views/flow/base-edge.d.ts +0 -1
  137. package/dist/src/views/flow/base-edge.js +25 -10
  138. package/dist/src/views/flow/flow-loader.d.ts +0 -1
  139. package/dist/src/views/flow/flow-loader.js +1 -1
  140. package/dist/src/views/flow/flow-view.d.ts +7 -2
  141. package/dist/src/views/flow/flow-view.js +44 -20
  142. package/dist/src/views/flow/hooks/use-get-flow-state.d.ts +14 -5
  143. package/dist/src/views/flow/hooks/use-get-flow-state.js +17 -8
  144. package/dist/src/views/flow/hooks/use-organize-nodes.d.ts +0 -1
  145. package/dist/src/views/flow/hooks/use-save-workflow-config.d.ts +9 -0
  146. package/dist/src/views/flow/hooks/use-save-workflow-config.js +23 -0
  147. package/dist/src/views/flow/legend.d.ts +3 -3
  148. package/dist/src/views/flow/legend.js +49 -39
  149. package/dist/src/views/flow/node-organizer.d.ts +1 -1
  150. package/dist/src/views/flow/node-organizer.js +4 -2
  151. package/dist/src/views/flow/nodes/api-flow-node.d.ts +0 -1
  152. package/dist/src/views/flow/nodes/event-flow-node.d.ts +0 -1
  153. package/dist/src/views/flow/nodes/language-indicator.d.ts +0 -1
  154. package/dist/src/views/flow/nodes/language-indicator.js +7 -7
  155. package/dist/src/views/flow/nodes/nodes.types.d.ts +39 -6
  156. package/dist/src/views/flow/nodes/noop-flow-node.d.ts +0 -1
  157. package/dist/tailwind.config.js +9 -77
  158. package/dist/tsconfig.app.tsbuildinfo +1 -1
  159. package/dist/tsconfig.node.tsbuildinfo +1 -1
  160. package/package.json +36 -42
  161. package/postcss.config.mjs +9 -0
  162. package/dist/index.d.ts.map +0 -1
  163. package/dist/middleware.d.ts.map +0 -1
  164. package/dist/src/components/app-sidebar.d.ts.map +0 -1
  165. package/dist/src/components/log-console.d.ts.map +0 -1
  166. package/dist/src/components/log-console.js +0 -20
  167. package/dist/src/components/log-level-badge.d.ts.map +0 -1
  168. package/dist/src/components/ui/badge.d.ts.map +0 -1
  169. package/dist/src/components/ui/button.d.ts.map +0 -1
  170. package/dist/src/components/ui/collapsible.d.ts.map +0 -1
  171. package/dist/src/components/ui/dialog.d.ts.map +0 -1
  172. package/dist/src/components/ui/input.d.ts.map +0 -1
  173. package/dist/src/components/ui/label.d.ts.map +0 -1
  174. package/dist/src/components/ui/select.d.ts.map +0 -1
  175. package/dist/src/components/ui/separator.d.ts.map +0 -1
  176. package/dist/src/components/ui/sheet.d.ts.map +0 -1
  177. package/dist/src/components/ui/sidebar.d.ts.map +0 -1
  178. package/dist/src/components/ui/skeleton.d.ts.map +0 -1
  179. package/dist/src/components/ui/switch.d.ts.map +0 -1
  180. package/dist/src/components/ui/table.d.ts.map +0 -1
  181. package/dist/src/components/ui/textarea.d.ts.map +0 -1
  182. package/dist/src/components/ui/tooltip.d.ts.map +0 -1
  183. package/dist/src/hooks/use-list-flows.d.ts.map +0 -1
  184. package/dist/src/hooks/use-log-listener.d.ts.map +0 -1
  185. package/dist/src/hooks/use-mobile.d.ts.map +0 -1
  186. package/dist/src/lib/utils.d.ts.map +0 -1
  187. package/dist/src/main.d.ts.map +0 -1
  188. package/dist/src/publicComponents/api-node.d.ts.map +0 -1
  189. package/dist/src/publicComponents/base-handle.d.ts.map +0 -1
  190. package/dist/src/publicComponents/base-node.d.ts.map +0 -1
  191. package/dist/src/publicComponents/emits.d.ts.map +0 -1
  192. package/dist/src/publicComponents/event-node.d.ts.map +0 -1
  193. package/dist/src/publicComponents/node-props.d.ts.map +0 -1
  194. package/dist/src/publicComponents/noop-node.d.ts.map +0 -1
  195. package/dist/src/publicComponents/subscribe.d.ts.map +0 -1
  196. package/dist/src/route-wrapper.d.ts.map +0 -1
  197. package/dist/src/routeTree.gen.d.ts +0 -53
  198. package/dist/src/routeTree.gen.d.ts.map +0 -1
  199. package/dist/src/routeTree.gen.js +0 -45
  200. package/dist/src/routes/__root.d.ts +0 -2
  201. package/dist/src/routes/__root.d.ts.map +0 -1
  202. package/dist/src/routes/__root.js +0 -15
  203. package/dist/src/routes/flow/$id.d.ts +0 -4
  204. package/dist/src/routes/flow/$id.d.ts.map +0 -1
  205. package/dist/src/routes/flow/$id.js +0 -15
  206. package/dist/src/routes/index.d.ts.map +0 -1
  207. package/dist/src/stores/use-logs.d.ts.map +0 -1
  208. package/dist/src/views/flow/arrow-head.d.ts.map +0 -1
  209. package/dist/src/views/flow/base-edge.d.ts.map +0 -1
  210. package/dist/src/views/flow/flow-loader.d.ts.map +0 -1
  211. package/dist/src/views/flow/flow-view.d.ts.map +0 -1
  212. package/dist/src/views/flow/hooks/use-get-flow-state.d.ts.map +0 -1
  213. package/dist/src/views/flow/hooks/use-organize-nodes.d.ts.map +0 -1
  214. package/dist/src/views/flow/legend.d.ts.map +0 -1
  215. package/dist/src/views/flow/node-organizer.d.ts.map +0 -1
  216. package/dist/src/views/flow/nodes/api-flow-node.d.ts.map +0 -1
  217. package/dist/src/views/flow/nodes/event-flow-node.d.ts.map +0 -1
  218. package/dist/src/views/flow/nodes/json-schema-form.d.ts +0 -9
  219. package/dist/src/views/flow/nodes/json-schema-form.d.ts.map +0 -1
  220. package/dist/src/views/flow/nodes/json-schema-form.js +0 -35
  221. package/dist/src/views/flow/nodes/language-indicator.d.ts.map +0 -1
  222. package/dist/src/views/flow/nodes/nodes.types.d.ts.map +0 -1
  223. package/dist/src/views/flow/nodes/noop-flow-node.d.ts.map +0 -1
  224. package/dist/tailwind.config.d.ts +0 -4
  225. package/dist/tailwind.config.d.ts.map +0 -1
  226. package/dist/vite.config.d.ts +0 -11
  227. package/dist/vite.config.d.ts.map +0 -1
  228. package/dist/vite.config.js +0 -18
  229. package/eslint.config.js +0 -28
  230. package/index.html +0 -19
  231. package/index.tsx +0 -10
  232. package/middleware.ts +0 -48
  233. package/postcss.config.js +0 -6
  234. package/src/assets/.empty +0 -0
  235. package/src/components/app-sidebar.tsx +0 -55
  236. package/src/components/log-console.tsx +0 -76
  237. package/src/components/log-level-badge.tsx +0 -12
  238. package/src/components/ui/badge.tsx +0 -31
  239. package/src/components/ui/button.tsx +0 -47
  240. package/src/components/ui/collapsible.tsx +0 -9
  241. package/src/components/ui/dialog.tsx +0 -120
  242. package/src/components/ui/input.tsx +0 -21
  243. package/src/components/ui/label.tsx +0 -26
  244. package/src/components/ui/select.tsx +0 -157
  245. package/src/components/ui/separator.tsx +0 -22
  246. package/src/components/ui/sheet.tsx +0 -106
  247. package/src/components/ui/sidebar.tsx +0 -637
  248. package/src/components/ui/skeleton.tsx +0 -7
  249. package/src/components/ui/switch.tsx +0 -27
  250. package/src/components/ui/table.tsx +0 -76
  251. package/src/components/ui/textarea.tsx +0 -22
  252. package/src/components/ui/tooltip.tsx +0 -32
  253. package/src/hooks/use-list-flows.tsx +0 -20
  254. package/src/hooks/use-log-listener.tsx +0 -32
  255. package/src/hooks/use-mobile.tsx +0 -19
  256. package/src/index.css +0 -190
  257. package/src/lib/utils.ts +0 -6
  258. package/src/main.tsx +0 -28
  259. package/src/publicComponents/api-node.tsx +0 -28
  260. package/src/publicComponents/base-handle.tsx +0 -43
  261. package/src/publicComponents/base-node.tsx +0 -57
  262. package/src/publicComponents/emits.tsx +0 -22
  263. package/src/publicComponents/event-node.tsx +0 -36
  264. package/src/publicComponents/node-props.tsx +0 -15
  265. package/src/publicComponents/noop-node.tsx +0 -21
  266. package/src/publicComponents/subscribe.tsx +0 -19
  267. package/src/route-wrapper.tsx +0 -9
  268. package/src/routeTree.gen.ts +0 -109
  269. package/src/routes/__root.tsx +0 -26
  270. package/src/routes/flow/$id.tsx +0 -21
  271. package/src/routes/index.tsx +0 -13
  272. package/src/stores/use-logs.ts +0 -22
  273. package/src/views/flow/arrow-head.tsx +0 -13
  274. package/src/views/flow/base-edge.tsx +0 -31
  275. package/src/views/flow/flow-loader.tsx +0 -3
  276. package/src/views/flow/flow-view.tsx +0 -72
  277. package/src/views/flow/hooks/use-get-flow-state.tsx +0 -93
  278. package/src/views/flow/hooks/use-organize-nodes.ts +0 -60
  279. package/src/views/flow/legend.tsx +0 -96
  280. package/src/views/flow/node-organizer.tsx +0 -70
  281. package/src/views/flow/nodes/api-flow-node.tsx +0 -6
  282. package/src/views/flow/nodes/event-flow-node.tsx +0 -6
  283. package/src/views/flow/nodes/json-schema-form.tsx +0 -110
  284. package/src/views/flow/nodes/language-indicator.tsx +0 -74
  285. package/src/views/flow/nodes/nodes.types.ts +0 -36
  286. package/src/views/flow/nodes/noop-flow-node.tsx +0 -6
  287. package/src/vite-env.d.ts +0 -1
  288. package/tailwind.config.ts +0 -75
  289. package/tsconfig.app.json +0 -32
  290. package/tsconfig.json +0 -14
  291. package/tsconfig.node.json +0 -32
  292. package/tsconfig.node.tsbuildinfo +0 -1
  293. package/vite.config.ts +0 -14
  294. /package/{components.json → dist/components.json} +0 -0
  295. /package/{public → dist/public}/.empty +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Motia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,50 +1,105 @@
1
- # React + TypeScript + Vite
1
+ # @motiadev/workbench
2
2
 
3
- This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
3
+ A web-based interface for building, visualizing, and managing Motia workflows.
4
4
 
5
- Currently, two official plugins are available:
5
+ ## Overview
6
6
 
7
- - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
8
- - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
7
+ `@motiadev/workbench` provides a powerful visual interface for Motia workflows, offering:
9
8
 
10
- ## Expanding the ESLint configuration
9
+ - Flow visualization with interactive diagrams
10
+ - Real-time log monitoring
11
+ - State inspection and management
12
+ - API testing capabilities
11
13
 
12
- If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
14
+ ## Installation
13
15
 
14
- - Configure the top-level `parserOptions` property like this:
16
+ ```bash
17
+ npm install @motiadev/workbench
18
+ # or
19
+ yarn add @motiadev/workbench
20
+ # or
21
+ pnpm add @motiadev/workbench
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ The Workbench is automatically integrated when you run a Motia project in development mode:
27
+
28
+ ```bash
29
+ npx motia dev
30
+ ```
31
+
32
+ This starts the development server and makes the Workbench available at `http://localhost:3000` by default.
33
+
34
+ ## Features
35
+
36
+ ### Flow Visualization
37
+
38
+ Visualize your workflows as interactive diagrams, showing the connections between steps and the flow of events through your application.
39
+
40
+ ### Log Monitoring
41
+
42
+ Monitor logs in real-time with filtering capabilities, log level indicators, and detailed log inspection.
43
+
44
+ ### State Management
45
+
46
+ Inspect and manage application state, with support for viewing complex nested objects and state changes over time.
15
47
 
16
- ```js
17
- export default tseslint.config({
18
- languageOptions: {
19
- // other options...
20
- parserOptions: {
21
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
22
- tsconfigRootDir: import.meta.dirname,
23
- },
24
- },
25
- })
48
+ ### API Testing
49
+
50
+ Test API endpoints directly from the Workbench interface, with support for different HTTP methods and request bodies.
51
+
52
+ ## Components
53
+
54
+ The package exports several components that can be used to customize the visualization of your workflows:
55
+
56
+ ```typescript
57
+ import {
58
+ EventNode,
59
+ ApiNode,
60
+ NoopNode,
61
+ BaseNode,
62
+ BaseHandle
63
+ } from '@motiadev/workbench'
26
64
  ```
27
65
 
28
- - Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
29
- - Optionally add `...tseslint.configs.stylisticTypeChecked`
30
- - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
31
-
32
- ```js
33
- // eslint.config.js
34
- import react from 'eslint-plugin-react'
35
-
36
- export default tseslint.config({
37
- // Set the react version
38
- settings: { react: { version: '18.3' } },
39
- plugins: {
40
- // Add the react plugin
41
- react,
42
- },
43
- rules: {
44
- // other rules...
45
- // Enable its recommended rules
46
- ...react.configs.recommended.rules,
47
- ...react.configs['jsx-runtime'].rules,
48
- },
49
- })
66
+ ### Node Components
67
+
68
+ - `EventNode`: Visualizes event-based steps
69
+ - `ApiNode`: Visualizes API endpoint steps
70
+ - `NoopNode`: A placeholder node with no specific functionality
71
+ - `BaseNode`: Base component for creating custom node types
72
+ - `BaseHandle`: Connection point component for nodes
73
+
74
+ ## Customization
75
+
76
+ You can customize the appearance and behavior of the Workbench by creating custom node components:
77
+
78
+ ```typescript
79
+ import { BaseNode, Position } from '@motiadev/workbench'
80
+
81
+ export const CustomNode = ({ data, ...props }) => {
82
+ return (
83
+ <BaseNode
84
+ {...props}
85
+ title="Custom Node"
86
+ color="#8B5CF6"
87
+ >
88
+ <div className="p-4">
89
+ {data.customContent}
90
+ </div>
91
+ </BaseNode>
92
+ )
93
+ }
50
94
  ```
95
+
96
+ ## Technical Details
97
+
98
+ - Built with React and TypeScript
99
+ - Uses [XY Flow](https://xyflow.com/) for flow visualization
100
+ - Styled with Tailwind CSS and shadcn/ui components
101
+ - Supports real-time updates via WebSockets
102
+
103
+ ## License
104
+
105
+ This package is part of the Motia framework and is licensed under the same terms.
package/dist/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # @motiadev/workbench
2
+
3
+ A web-based interface for building, visualizing, and managing Motia workflows.
4
+
5
+ ## Overview
6
+
7
+ `@motiadev/workbench` provides a powerful visual interface for Motia workflows, offering:
8
+
9
+ - Flow visualization with interactive diagrams
10
+ - Real-time log monitoring
11
+ - State inspection and management
12
+ - API testing capabilities
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @motiadev/workbench
18
+ # or
19
+ yarn add @motiadev/workbench
20
+ # or
21
+ pnpm add @motiadev/workbench
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ The Workbench is automatically integrated when you run a Motia project in development mode:
27
+
28
+ ```bash
29
+ npx motia dev
30
+ ```
31
+
32
+ This starts the development server and makes the Workbench available at `http://localhost:3000` by default.
33
+
34
+ ## Features
35
+
36
+ ### Flow Visualization
37
+
38
+ Visualize your workflows as interactive diagrams, showing the connections between steps and the flow of events through your application.
39
+
40
+ ### Log Monitoring
41
+
42
+ Monitor logs in real-time with filtering capabilities, log level indicators, and detailed log inspection.
43
+
44
+ ### State Management
45
+
46
+ Inspect and manage application state, with support for viewing complex nested objects and state changes over time.
47
+
48
+ ### API Testing
49
+
50
+ Test API endpoints directly from the Workbench interface, with support for different HTTP methods and request bodies.
51
+
52
+ ## Components
53
+
54
+ The package exports several components that can be used to customize the visualization of your workflows:
55
+
56
+ ```typescript
57
+ import {
58
+ EventNode,
59
+ ApiNode,
60
+ NoopNode,
61
+ BaseNode,
62
+ BaseHandle
63
+ } from '@motiadev/workbench'
64
+ ```
65
+
66
+ ### Node Components
67
+
68
+ - `EventNode`: Visualizes event-based steps
69
+ - `ApiNode`: Visualizes API endpoint steps
70
+ - `NoopNode`: A placeholder node with no specific functionality
71
+ - `BaseNode`: Base component for creating custom node types
72
+ - `BaseHandle`: Connection point component for nodes
73
+
74
+ ## Customization
75
+
76
+ You can customize the appearance and behavior of the Workbench by creating custom node components:
77
+
78
+ ```typescript
79
+ import { BaseNode, Position } from '@motiadev/workbench'
80
+
81
+ export const CustomNode = ({ data, ...props }) => {
82
+ return (
83
+ <BaseNode
84
+ {...props}
85
+ title="Custom Node"
86
+ color="#8B5CF6"
87
+ >
88
+ <div className="p-4">
89
+ {data.customContent}
90
+ </div>
91
+ </BaseNode>
92
+ )
93
+ }
94
+ ```
95
+
96
+ ## Technical Details
97
+
98
+ - Built with React and TypeScript
99
+ - Uses [XY Flow](https://xyflow.com/) for flow visualization
100
+ - Styled with Tailwind CSS and shadcn/ui components
101
+ - Supports real-time updates via WebSockets
102
+
103
+ ## License
104
+
105
+ This package is part of the Motia framework and is licensed under the same terms.
package/dist/index.d.ts CHANGED
@@ -5,6 +5,5 @@ export { BaseNode } from './src/publicComponents/base-node';
5
5
  export { BaseHandle } from './src/publicComponents/base-handle';
6
6
  export { Position } from '@xyflow/react';
7
7
  export type { EventNodeData, ApiNodeData } from './src/views/flow/nodes/nodes.types';
8
- export * from './src/publicComponents/node-props';
8
+ export type { ApiNodeProps, BaseNodeProps, CronNodeProps, EventNodeProps, NoopNodeProps, } from './src/publicComponents/node-props';
9
9
  export { Button } from './src/components/ui/button';
10
- //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,31 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+
6
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
7
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
8
+ <link
9
+ href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
10
+ rel="stylesheet"
11
+ />
12
+ <script src="https://cdn.amplitude.com/libs/analytics-browser-2.11.1-min.js.gz"></script>
13
+ <script>
14
+ window.amplitude.init('ab2408031a38aa5cb85587a27ecfc69c', {
15
+ autocapture: {
16
+ fileDownloads: false,
17
+ formInteractions: false,
18
+ },
19
+ fetchRemoteConfig: true,
20
+ recording: false
21
+ })
22
+ </script>
23
+
24
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
25
+ <title>Motia</title>
26
+ </head>
27
+ <body class="dark">
28
+ <div id="root"></div>
29
+ <script type="module" src="/src/main.js"></script>
30
+ </body>
31
+ </html>
package/dist/index.js CHANGED
@@ -4,5 +4,4 @@ export { NoopNode } from './src/publicComponents/noop-node';
4
4
  export { BaseNode } from './src/publicComponents/base-node';
5
5
  export { BaseHandle } from './src/publicComponents/base-handle';
6
6
  export { Position } from '@xyflow/react';
7
- export * from './src/publicComponents/node-props';
8
7
  export { Button } from './src/components/ui/button';
@@ -1,2 +1,2 @@
1
- export declare const applyMiddleware: (app: any) => Promise<void>;
2
- //# sourceMappingURL=middleware.d.ts.map
1
+ import type { Express } from 'express';
2
+ export declare const applyMiddleware: (app: Express) => Promise<void>;
@@ -4,39 +4,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.applyMiddleware = void 0;
7
- const tailwind_config_1 = __importDefault(require("./tailwind.config"));
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const vite_1 = require("vite");
10
+ const plugin_react_1 = __importDefault(require("@vitejs/plugin-react"));
8
11
  const applyMiddleware = async (app) => {
9
- // NOTE: this is needed since vite is an ESM https://vite.dev/guide/troubleshooting.html#vite-cjs-node-api-deprecated
10
- const fs = await import('fs');
11
- const path = await import('path');
12
- const { default: Express } = await import('express');
13
- const { createServer } = await import('vite');
14
- const { default: autoprefixer } = await import('autoprefixer');
15
- const { default: tailwindcss } = await import('tailwindcss');
16
- const vite = await createServer({
12
+ const vite = await (0, vite_1.createServer)({
17
13
  appType: 'spa',
18
14
  root: __dirname,
19
15
  server: {
20
16
  middlewareMode: true,
17
+ host: true,
21
18
  fs: {
22
- allow: [__dirname, path.join(process.cwd(), './steps')],
19
+ allow: [__dirname, path_1.default.join(process.cwd(), './steps')],
23
20
  },
24
21
  },
25
22
  resolve: {
26
- alias: { '@': path.resolve(__dirname, './src') },
27
- },
28
- css: {
29
- postcss: {
30
- plugins: [autoprefixer(), tailwindcss(tailwind_config_1.default)],
31
- },
23
+ alias: { '@': path_1.default.resolve(__dirname, './src') },
32
24
  },
25
+ plugins: [(0, plugin_react_1.default)()],
33
26
  });
34
27
  app.use(vite.middlewares);
35
28
  app.use('*', async (req, res, next) => {
36
29
  const url = req.originalUrl;
37
30
  console.log('[UI] Request', { url });
38
31
  try {
39
- const index = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8');
32
+ const index = fs_1.default.readFileSync(path_1.default.resolve(__dirname, 'index.html'), 'utf-8');
40
33
  const html = await vite.transformIndexHtml(url, index);
41
34
  res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
42
35
  }
@@ -0,0 +1,9 @@
1
+ import path from 'path'
2
+
3
+ export default {
4
+ plugins: {
5
+ "@tailwindcss/postcss": {
6
+ base: path.join(import.meta.dirname, './src'),
7
+ },
8
+ }
9
+ }
@@ -1,2 +1 @@
1
1
  export declare const AppSidebar: () => import("react/jsx-runtime").JSX.Element;
2
- //# sourceMappingURL=app-sidebar.d.ts.map
@@ -1,13 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useListFlows } from '@/hooks/use-list-flows';
3
- import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, } from './ui/sidebar';
4
- import { Workflow } from 'lucide-react';
5
- import { Link, useMatchRoute } from '@tanstack/react-router';
3
+ import { File, Link2, Logs, Workflow } from 'lucide-react';
4
+ import { Link, useLocation } from 'react-router';
5
+ import { Sidebar, SidebarButton, SidebarGroup } from './ui/sidebar';
6
+ import { Badge } from './ui/badge';
7
+ import { useLogs } from '@/stores/use-logs';
8
+ const BadgeCount = () => {
9
+ const unreadLogsCount = useLogs((state) => state.unreadLogsCount);
10
+ if (!unreadLogsCount) {
11
+ return null;
12
+ }
13
+ return _jsx(Badge, { variant: "red-rounded", children: unreadLogsCount });
14
+ };
6
15
  export const AppSidebar = () => {
7
16
  const { flows } = useListFlows();
8
- const matchRoute = useMatchRoute();
9
- const isActive = (flowId) => {
10
- return !!matchRoute({ to: '/flow/$id', params: { id: flowId } });
11
- };
12
- return (_jsxs(Sidebar, { children: [_jsx(SidebarHeader, {}), _jsx(SidebarContent, { children: _jsxs(SidebarGroup, { children: [_jsx(SidebarGroupLabel, { children: "Flows" }), _jsx(SidebarGroupContent, { children: _jsx(SidebarMenu, { children: flows.map((flow) => (_jsx(SidebarMenuItem, { children: _jsx(SidebarMenuButton, { asChild: true, className: "cursor-pointer", isActive: isActive(flow.id), children: _jsxs(Link, { to: "/flow/$id", params: { id: flow.id }, className: "flex items-center gap-2", "data-testid": `flow-link-${flow.id}`, children: [_jsx(Workflow, {}), _jsx("span", { children: flow.name })] }) }) }, flow.id))) }) })] }) }), _jsx(SidebarFooter, {})] }));
17
+ const { pathname } = useLocation();
18
+ const isActive = (flowId) => pathname.includes(`/flow/${flowId}`);
19
+ return (_jsxs(Sidebar, { children: [_jsxs(SidebarGroup, { testId: "motia-title", title: "Motia", children: [_jsx(Link, { "data-testid": "logs-link", to: "/logs", children: _jsxs(SidebarButton, { isActive: pathname === '/logs', icon: _jsx(Logs, { className: "w-4 h-4" }), children: ["Logs", pathname !== '/logs' && _jsx(BadgeCount, {})] }) }), _jsx(Link, { "data-testid": "states-link", to: "/states", children: _jsx(SidebarButton, { isActive: pathname === '/states', icon: _jsx(File, { className: "w-4 h-4" }), children: "States" }) }), _jsx(Link, { "data-testid": "endpoints-link", to: "/endpoints", children: _jsx(SidebarButton, { isActive: pathname === '/endpoints', icon: _jsx(Link2, { className: "w-4 h-4" }), children: "Endpoints" }) })] }), _jsx(SidebarGroup, { testId: "flows-title", title: "Flows", children: flows.map((flow) => (_jsx(Link, { "data-testid": `flow-${flow.name}-link`, to: `/flow/${flow.id}`, children: _jsx(SidebarButton, { isActive: isActive(flow.id), icon: _jsx(Workflow, { className: "w-4 h-4" }), children: flow.name }) }, flow.id))) })] }));
13
20
  };
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import { type VariantProps } from 'class-variance-authority';
3
+ declare const badgeVariants: (props?: ({
4
+ variant?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS" | null | undefined;
5
+ defaultVariants?: "variant" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
8
+ }
9
+ export declare function EndpointBadge({ className, variant, ...props }: BadgeProps): import("react/jsx-runtime").JSX.Element;
10
+ export {};
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cva } from 'class-variance-authority';
3
+ import { cn } from '@/lib/utils';
4
+ const badgeVariants = cva('inline-flex items-center rounded-lg border px-2 py-1 text-xs font-bold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', {
5
+ variants: {
6
+ variant: {
7
+ POST: 'bg-sky-500/50 text-sky-100',
8
+ GET: 'bg-lime-500/50 text-lime-100',
9
+ PUT: 'bg-yellow-500/50 text-yellow-100',
10
+ DELETE: 'bg-red-500/50 text-red-100',
11
+ PATCH: 'bg-yellow-500/50 text-yellow-100',
12
+ HEAD: 'bg-blue-500/50 text-blue-100',
13
+ OPTIONS: 'bg-purple-500/50 text-purple-100',
14
+ },
15
+ defaultVariants: {
16
+ variant: 'bg-blue-500/50 text-blue-100',
17
+ },
18
+ },
19
+ });
20
+ export function EndpointBadge({ className, variant, ...props }) {
21
+ return _jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
22
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { ApiEndpoint } from './hooks/use-get-endpoints';
3
+ type Props = {
4
+ endpoint: ApiEndpoint;
5
+ onClose: () => void;
6
+ };
7
+ export declare const EndpointCall: React.FC<Props>;
8
+ export {};
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from 'react';
3
+ import { Loader2, Play, X } from 'lucide-react';
4
+ import { Button } from '../ui/button';
5
+ import { Input } from '../ui/input';
6
+ import { Textarea } from '../ui/textarea';
7
+ import { EndpointBadge } from './endpoint-badge';
8
+ import { useJsonSchemaToJson } from './hooks/use-json-schema-to-json';
9
+ import { usePathParams } from './hooks/use-path-params';
10
+ import { useStateStream } from './hooks/use-state-stream';
11
+ export const EndpointCall = ({ endpoint, onClose }) => {
12
+ const shouldHaveBody = ['post', 'put', 'patch'].includes(endpoint.method.toLowerCase());
13
+ const [isRequestLoading, setIsRequestLoading] = useState(false);
14
+ const [responseCode, setResponseCode] = useState();
15
+ const [responseBody, setResponseBody] = useState();
16
+ const [executionTime, setExecutionTime] = useState();
17
+ const { body, setBody } = useJsonSchemaToJson(endpoint.bodySchema);
18
+ const pathParams = usePathParams(endpoint.path);
19
+ const [pathParamsValues, setPathParamsValues] = useState(pathParams?.reduce((acc, param) => ({ ...acc, [param]: '' }), {}));
20
+ const [queryParamsValues, setQueryParamsValues] = useState(endpoint.queryParams?.reduce((acc, param) => ({ ...acc, [param.name]: '' }), {}) ?? {});
21
+ const { data: responseBodyData, isStreamed } = useStateStream(responseBody);
22
+ const isPlayEnabled = useMemo(() => {
23
+ if (!pathParams)
24
+ return true;
25
+ return pathParams?.every((param) => pathParamsValues[param]);
26
+ }, [pathParams, pathParamsValues]);
27
+ const onPathParamChange = (param, value) => {
28
+ setPathParamsValues((prev) => ({ ...prev, [param]: value }));
29
+ };
30
+ const onQueryParamChange = (param, value) => {
31
+ setQueryParamsValues((prev) => ({ ...prev, [param]: value }));
32
+ };
33
+ const handleRequest = async () => {
34
+ setIsRequestLoading(true);
35
+ const startTime = Date.now();
36
+ const path = new URL(window.location.origin +
37
+ pathParams.reduce((acc, param) => {
38
+ return acc.replace(`:${param}`, pathParamsValues[param]);
39
+ }, endpoint.path));
40
+ for (const [key, value] of Object.entries(queryParamsValues)) {
41
+ path.searchParams.set(key, value);
42
+ }
43
+ const response = await fetch(path.toString(), {
44
+ method: endpoint.method,
45
+ headers: { 'Content-Type': 'application/json' },
46
+ body: endpoint.method === 'GET' ? null : body,
47
+ });
48
+ const endTime = Date.now();
49
+ const executionTime = endTime - startTime;
50
+ const json = await response.json();
51
+ setResponseCode(response.status);
52
+ setResponseBody(json);
53
+ setExecutionTime(executionTime);
54
+ setIsRequestLoading(false);
55
+ };
56
+ return (_jsxs("div", { className: "flex flex-col gap-2 overflow-y-auto", children: [_jsxs("div", { className: "text-xs flex flex-row gap-2 items-center justify-between w-full", children: [_jsx("span", { className: "font-bold", children: "Request" }), _jsx("div", { className: "flex flex-row gap-2 items-center hover:bg-white/10 rounded-md p-1", children: _jsx(X, { className: "cursor-pointer w-4 h-4", onClick: onClose }) })] }), _jsxs("div", { className: "flex flex-row gap-2 items-center", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-bold", children: endpoint.path })] }), _jsx("span", { className: "text-xs text-muted-foreground", children: endpoint.description }), !!pathParams.length && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Path Params" }), _jsx("div", { className: "flex flex-col gap-4", children: pathParams.map((param) => (_jsxs("div", { className: "text-xs", children: [_jsx("div", { className: "font-bold mb-2", children: param }), _jsx(Input, { className: "w-full", value: pathParamsValues[param], onChange: (e) => onPathParamChange(param, e.target.value) })] }, param))) })] })), !!endpoint.queryParams?.length && (_jsxs("div", { className: "flex flex-col gap-2 p-4 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Query Params" }), _jsx("div", { className: "flex flex-col gap-4", children: endpoint.queryParams.map((param) => (_jsxs("div", { className: "text-xs", children: [_jsx("div", { className: "font-bold mb-2", children: param.name }), _jsx(Input, { className: "w-full", value: queryParamsValues[param.name], onChange: (e) => onQueryParamChange(param.name, e.target.value) })] }, param.name))) })] })), shouldHaveBody && (_jsxs("div", { className: "flex flex-col gap-2 rounded-lg bg-muted", children: [_jsx("span", { className: "text-xs font-bold", children: "Body" }), _jsx(Textarea, { className: "w-full font-mono font-medium min-h-[200px]", value: body, onChange: (e) => setBody(e.target.value) })] })), _jsxs(Button, { className: "w-fit", onClick: handleRequest, disabled: isRequestLoading || !isPlayEnabled, children: [isRequestLoading ? _jsx(Loader2, { className: "animate-spin" }) : _jsx(Play, {}), " Play"] }), responseCode !== undefined && (_jsxs("div", { className: "flex flex-col gap-2 rounded-lg bg-muted", children: [_jsxs("span", { className: "text-xs font-bold", children: [_jsx(EndpointBadge, { variant: responseCode >= 400 ? 'DELETE' : 'GET', children: responseCode }), " Execution time: ", _jsxs("span", { className: "text-muted-foreground", children: [executionTime, "ms"] })] }), isStreamed && (_jsxs("span", { className: "flex flex-row items-center font-medium text-muted-foreground text-xs", children: [_jsxs("span", { className: "ml-1 inline-block w-2 h-2 rounded-full bg-green-500 mr-2 relative", children: [_jsx("span", { className: "absolute inset-0 rounded-full bg-green-500 animate-[ping_1.5s_ease-in-out_infinite]" }), _jsx("span", { className: "absolute inset-0 rounded-full bg-green-500" })] }), "Object is being streamed, this is not the actual response from the API Endpoint"] })), _jsx("span", { className: "text-xs font-mono font-bold dark:bg-black/50 bg-white/50 p-2 rounded-lg whitespace-pre-wrap", children: JSON.stringify(responseBodyData, null, 2) })] }))] }));
57
+ };
@@ -0,0 +1 @@
1
+ export declare const Endpoints: () => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cva } from 'class-variance-authority';
3
+ import { useEffect, useState } from 'react';
4
+ import { EndpointBadge } from './endpoint-badge';
5
+ import { EndpointCall } from './endpoint-call';
6
+ import { useGetEndpoints } from './hooks/use-get-endpoints';
7
+ import { SelectedEndpoint } from './selected-endpoint';
8
+ const endpointVariants = cva('flex flex-col gap-2 font-mono p-2 rounded-lg cursor-pointer', {
9
+ variants: {
10
+ method: {
11
+ GET: 'bg-lime-500/20',
12
+ POST: 'bg-blue-500/20',
13
+ PUT: 'bg-yellow-500/20',
14
+ DELETE: 'bg-red-500/20',
15
+ PATCH: 'bg-yellow-500/20',
16
+ HEAD: 'bg-blue-500/20',
17
+ OPTIONS: 'bg-purple-500/20',
18
+ },
19
+ },
20
+ defaultVariants: { method: 'GET' },
21
+ });
22
+ export const Endpoints = () => {
23
+ const endpoints = useGetEndpoints();
24
+ const [selectedEndpoint, setSelectedEndpoint] = useState(null);
25
+ useEffect(() => {
26
+ setSelectedEndpoint((selected) => {
27
+ if (!selected)
28
+ return null;
29
+ const endpoint = endpoints.find((endpoint) => endpoint.method === selected.method && endpoint.path === selected.path);
30
+ return endpoint ?? null;
31
+ });
32
+ }, [endpoints]);
33
+ return (_jsxs("div", { className: "flex flex-row w-full h-screen", children: [_jsxs("div", { className: "flex flex-col gap-2 flex-1 m-4 mr-2 max-h-full overflow-y-auto", children: [_jsxs("header", { children: [_jsx("h1", { className: "text-2xl font-bold", children: "API Endpoints" }), _jsx("span", { className: "text-sm text-zinc-400", children: "Check all API endpoints" })] }), endpoints.map((endpoint) => (_jsxs("div", { className: endpointVariants({ method: endpoint.method }), onClick: () => setSelectedEndpoint(endpoint), children: [_jsxs("div", { className: "flex flex-row gap-2 items-center", children: [_jsx(EndpointBadge, { variant: endpoint.method, children: endpoint.method.toUpperCase() }), _jsx("span", { className: "text-md font-bold", children: endpoint.path }), !selectedEndpoint && _jsx("span", { className: "text-xs text-muted-foreground", children: endpoint.description })] }), selectedEndpoint && _jsx("span", { className: "text-xs text-muted-foreground", children: endpoint.description }), selectedEndpoint === endpoint && _jsx(SelectedEndpoint, { endpoint: selectedEndpoint })] }, `${endpoint.method} ${endpoint.path}`)))] }), selectedEndpoint && (_jsx("div", { className: "flex flex-col gap-2 flex-1 m-4 ml-2 p-4 rounded-lg bg-muted", children: _jsx(EndpointCall, { endpoint: selectedEndpoint, onClose: () => setSelectedEndpoint(null) }) }))] }));
34
+ };
@@ -0,0 +1,15 @@
1
+ type ApiRouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
2
+ type QueryParam = {
3
+ name: string;
4
+ description: string;
5
+ };
6
+ export type ApiEndpoint = {
7
+ method: ApiRouteMethod;
8
+ path: string;
9
+ description?: string;
10
+ queryParams?: QueryParam[];
11
+ responseSchema?: Record<string, any>;
12
+ bodySchema?: Record<string, Record<string, any>>;
13
+ };
14
+ export declare const useGetEndpoints: () => ApiEndpoint[];
15
+ export {};
@@ -0,0 +1,8 @@
1
+ import { useStreamGroup } from '@motiadev/stream-client-react';
2
+ export const useGetEndpoints = () => {
3
+ const { data: endpoints } = useStreamGroup({
4
+ streamName: '__motia.api-endpoints',
5
+ groupId: 'default',
6
+ });
7
+ return endpoints;
8
+ };
@@ -0,0 +1,4 @@
1
+ export declare const useJsonSchemaToJson: (schema: Record<string, any> | undefined) => {
2
+ body: string;
3
+ setBody: import("react").Dispatch<import("react").SetStateAction<string>>;
4
+ };
@@ -0,0 +1,11 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { convertJsonSchemaToJson } from './utils';
3
+ export const useJsonSchemaToJson = (schema) => {
4
+ const [body, setBody] = useState('');
5
+ useEffect(() => {
6
+ if (schema) {
7
+ setBody(JSON.stringify(convertJsonSchemaToJson(schema), null, 2));
8
+ }
9
+ }, [schema]);
10
+ return { body, setBody };
11
+ };
@@ -0,0 +1 @@
1
+ export declare const usePathParams: (path: string) => string[];
@@ -0,0 +1,4 @@
1
+ export const usePathParams = (path) => {
2
+ const pathParams = path.match(/:(\w+)/g);
3
+ return pathParams?.map((param) => param.slice(1)) ?? [];
4
+ };
@@ -0,0 +1,4 @@
1
+ export declare const useStateStream: (object: Record<string, any> | undefined) => {
2
+ data: {} | undefined;
3
+ isStreamed: boolean;
4
+ };
@@ -0,0 +1,8 @@
1
+ import { useStreamItem } from '@motiadev/stream-client-react';
2
+ export const useStateStream = (object) => {
3
+ const { data } = useStreamItem(object?.__motia);
4
+ return {
5
+ data: data || object,
6
+ isStreamed: !!data,
7
+ };
8
+ };