@motiadev/workbench 0.2.2 → 0.3.0-beta.79

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 (69) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/index.html +9 -9
  3. package/dist/index.js +1 -1
  4. package/dist/src/components/app-sidebar.js +2 -2
  5. package/dist/src/components/endpoints/endpoint-call.js +2 -2
  6. package/dist/src/components/header/header.js +1 -1
  7. package/dist/src/components/logs/log-console.js +1 -1
  8. package/dist/src/components/observability/events/code/function-call.d.ts +13 -0
  9. package/dist/src/components/observability/events/code/function-call.js +16 -0
  10. package/dist/src/components/observability/events/event-icon.d.ts +7 -0
  11. package/dist/src/components/observability/events/event-icon.js +16 -0
  12. package/dist/src/components/observability/events/trace-emit-event.d.ts +5 -0
  13. package/dist/src/components/observability/events/trace-emit-event.js +5 -0
  14. package/dist/src/components/observability/events/trace-event.d.ts +5 -0
  15. package/dist/src/components/observability/events/trace-event.js +20 -0
  16. package/dist/src/components/observability/events/trace-log-event.d.ts +5 -0
  17. package/dist/src/components/observability/events/trace-log-event.js +5 -0
  18. package/dist/src/components/observability/events/trace-state-event.d.ts +5 -0
  19. package/dist/src/components/observability/events/trace-state-event.js +5 -0
  20. package/dist/src/components/observability/events/trace-stream-event.d.ts +5 -0
  21. package/dist/src/components/observability/events/trace-stream-event.js +5 -0
  22. package/dist/src/components/observability/hooks/use-get-endtime.d.ts +2 -0
  23. package/dist/src/components/observability/hooks/use-get-endtime.js +15 -0
  24. package/dist/src/components/observability/observability-stats.d.ts +5 -0
  25. package/dist/src/components/observability/observability-stats.js +17 -0
  26. package/dist/src/components/observability/trace-item/trace-item-detail.d.ts +7 -0
  27. package/dist/src/components/observability/trace-item/trace-item-detail.js +10 -0
  28. package/dist/src/components/observability/trace-item/trace-item.d.ts +9 -0
  29. package/dist/src/components/observability/trace-item/trace-item.js +20 -0
  30. package/dist/src/components/observability/trace-status.d.ts +12 -0
  31. package/dist/src/components/observability/trace-status.js +43 -0
  32. package/dist/src/components/observability/trace-timeline.d.ts +6 -0
  33. package/dist/src/components/observability/trace-timeline.js +17 -0
  34. package/dist/src/components/observability/traces-groups.d.ts +9 -0
  35. package/dist/src/components/observability/traces-groups.js +15 -0
  36. package/dist/src/components/ui/card.d.ts +8 -0
  37. package/dist/src/components/ui/card.js +16 -0
  38. package/dist/src/components/ui/navigation-menu.d.ts +9 -10
  39. package/dist/src/components/ui/navigation-menu.js +9 -10
  40. package/dist/src/components/ui/scroll-area.d.ts +5 -0
  41. package/dist/src/components/ui/scroll-area.js +9 -0
  42. package/dist/src/components/ui/sheet.d.ts +1 -1
  43. package/dist/src/components/ui/sidebar.js +1 -1
  44. package/dist/src/components/ui/tabs.d.ts +7 -0
  45. package/dist/src/components/ui/tabs.js +12 -0
  46. package/dist/src/hooks/use-fetch-flows.d.ts +5 -0
  47. package/dist/src/hooks/use-fetch-flows.js +17 -0
  48. package/dist/src/hooks/use-list-flows.d.ts +3 -2
  49. package/dist/src/index.css +2 -155
  50. package/dist/src/lib/utils.d.ts +1 -0
  51. package/dist/src/lib/utils.js +7 -0
  52. package/dist/src/main.js +2 -1
  53. package/dist/src/routes/flow.js +2 -13
  54. package/dist/src/routes/index.js +2 -2
  55. package/dist/src/routes/traces-page.d.ts +1 -0
  56. package/dist/src/routes/traces-page.js +14 -0
  57. package/dist/src/types/observability.d.ts +78 -0
  58. package/dist/src/types/observability.js +1 -0
  59. package/dist/src/views/flow/flow-view.js +2 -17
  60. package/dist/src/views/flow/hooks/use-get-flow-state.d.ts +5 -2
  61. package/dist/src/views/flow/hooks/use-get-flow-state.js +97 -27
  62. package/dist/src/views/flow/hooks/use-save-workflow-config.d.ts +2 -9
  63. package/dist/src/views/flow/hooks/use-save-workflow-config.js +5 -6
  64. package/dist/src/views/flow/legend.js +1 -1
  65. package/dist/src/views/flow/node-organizer.js +4 -2
  66. package/dist/tsconfig.app.tsbuildinfo +1 -1
  67. package/package.json +12 -6
  68. package/dist/src/components/ui/button.d.ts +0 -11
  69. package/dist/src/components/ui/button.js +0 -33
@@ -1,38 +1,55 @@
1
1
  import { useEdgesState, useNodesState } from '@xyflow/react';
2
- import { useEffect, useState } from 'react';
2
+ import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
3
3
  import { ApiFlowNode } from '../nodes/api-flow-node';
4
4
  import { NoopFlowNode } from '../nodes/noop-flow-node';
5
5
  import { EventFlowNode } from '../nodes/event-flow-node';
6
6
  import { CronNode } from '@/publicComponents/cron-node';
7
+ import isEqual from 'fast-deep-equal';
8
+ import { useSaveWorkflowConfig } from '@/views/flow/hooks/use-save-workflow-config';
9
+ const DEFAULT_POSITION = { x: 0, y: 0 };
7
10
  const getNodePosition = (flowConfig, stepName) => {
8
- const position = flowConfig[stepName];
9
- return position || { x: 0, y: 0 };
11
+ return flowConfig?.config[stepName] || DEFAULT_POSITION;
12
+ };
13
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
14
+ const nodeComponentCache = new Map();
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ const BASE_NODE_TYPES = {
17
+ event: EventFlowNode,
18
+ api: ApiFlowNode,
19
+ noop: NoopFlowNode,
20
+ cron: CronNode,
10
21
  };
11
22
  async function importFlow(flow, flowConfig) {
12
23
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- const nodeTypes = {
14
- event: EventFlowNode,
15
- api: ApiFlowNode,
16
- noop: NoopFlowNode,
17
- cron: CronNode,
18
- };
19
- // Load custom node components if they exist
20
- for (const step of flow.steps) {
21
- if (step.nodeComponentPath) {
22
- const module = await import(/* @vite-ignore */ `/@fs/${step.nodeComponentPath}`);
23
- nodeTypes[step.nodeComponentPath] = module.Node ?? module.default;
24
+ const nodeTypes = { ...BASE_NODE_TYPES };
25
+ const customNodePromises = flow.steps
26
+ .filter((step) => step.nodeComponentPath)
27
+ .map(async (step) => {
28
+ const path = step.nodeComponentPath;
29
+ // Check cache first
30
+ if (nodeComponentCache.has(path)) {
31
+ nodeTypes[path] = nodeComponentCache.get(path);
32
+ return;
33
+ }
34
+ try {
35
+ const module = await import(/* @vite-ignore */ `/@fs/${path}`);
36
+ const component = module.Node ?? module.default;
37
+ nodeComponentCache.set(path, component);
38
+ nodeTypes[path] = component;
39
+ }
40
+ catch (error) {
41
+ console.error(`Failed to load custom node component: ${path}`, error);
24
42
  }
25
- }
26
- // Create nodes from steps
43
+ });
44
+ await Promise.all(customNodePromises);
27
45
  const nodes = flow.steps.map((step) => ({
28
46
  id: step.id,
29
- type: step.nodeComponentPath ? step.nodeComponentPath : step.type,
47
+ type: step.nodeComponentPath || step.type,
30
48
  filePath: step.filePath,
31
- position: step.filePath ? getNodePosition(flowConfig, step.filePath) : { x: 0, y: 0 },
49
+ position: step.filePath ? getNodePosition(flowConfig, step.filePath) : DEFAULT_POSITION,
32
50
  data: step,
33
51
  language: step.language,
34
52
  }));
35
- // Use the edges provided by the API, adding required ReactFlow properties
36
53
  const edges = flow.edges.map((edge) => ({
37
54
  ...edge,
38
55
  type: 'base',
@@ -41,17 +58,70 @@ async function importFlow(flow, flowConfig) {
41
58
  }
42
59
  export const useGetFlowState = (flow, flowConfig) => {
43
60
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- const [nodeTypes, setNodeTypes] = useState();
61
+ const [nodeTypes, setNodeTypes] = useState(BASE_NODE_TYPES);
45
62
  const [nodes, setNodes, onNodesChange] = useNodesState([]);
46
63
  const [edges, setEdges, onEdgesChange] = useEdgesState([]);
64
+ const saveConfig = useSaveWorkflowConfig();
65
+ const flowIdRef = useRef('');
66
+ const saveTimeoutRef = useRef(null);
67
+ const lastSavedConfigRef = useRef(null);
68
+ // eslint-disable-next-line react-hooks/exhaustive-deps
69
+ const memoizedFlowConfig = useMemo(() => flowConfig, [flowConfig?.id, flowConfig?.config]);
47
70
  useEffect(() => {
48
71
  if (!flow || flow.error)
49
72
  return;
50
- importFlow(flow, flowConfig).then(({ nodes, edges, nodeTypes }) => {
51
- setNodes(nodes);
52
- setEdges(edges);
53
- setNodeTypes(nodeTypes);
54
- });
55
- }, [flow, flowConfig, setNodes, setEdges, setNodeTypes]);
56
- return { nodes, edges, onNodesChange, onEdgesChange, nodeTypes };
73
+ if (isEqual(lastSavedConfigRef.current, memoizedFlowConfig?.config))
74
+ return;
75
+ lastSavedConfigRef.current = memoizedFlowConfig?.config;
76
+ flowIdRef.current = flow.id;
77
+ const importFlowAsync = async () => {
78
+ try {
79
+ const { nodes, edges, nodeTypes } = await importFlow(flow, flowConfig);
80
+ setNodes(nodes);
81
+ setEdges(edges);
82
+ setNodeTypes(nodeTypes);
83
+ }
84
+ catch (error) {
85
+ console.error('Failed to import flow:', error);
86
+ }
87
+ };
88
+ importFlowAsync();
89
+ }, [flow, memoizedFlowConfig, setNodes, setEdges, flowConfig]);
90
+ const saveFlowConfig = useCallback((nodesToSave) => {
91
+ if (saveTimeoutRef.current) {
92
+ clearTimeout(saveTimeoutRef.current);
93
+ }
94
+ saveTimeoutRef.current = setTimeout(async () => {
95
+ const steps = nodesToSave.reduce((acc, node) => {
96
+ if (node.data.filePath) {
97
+ acc[node.data.filePath] = {
98
+ x: Math.round(node.position.x),
99
+ y: Math.round(node.position.y),
100
+ };
101
+ }
102
+ return acc;
103
+ }, {});
104
+ if (!isEqual(steps, lastSavedConfigRef.current)) {
105
+ lastSavedConfigRef.current = steps;
106
+ const newConfig = { id: flowIdRef.current, config: steps };
107
+ try {
108
+ await saveConfig(newConfig);
109
+ }
110
+ catch (error) {
111
+ console.error('Failed to save flow config:', error);
112
+ }
113
+ }
114
+ }, 300);
115
+ }, [saveConfig]);
116
+ useEffect(() => {
117
+ if (nodes.length > 0) {
118
+ saveFlowConfig(nodes);
119
+ }
120
+ return () => {
121
+ if (saveTimeoutRef.current) {
122
+ clearTimeout(saveTimeoutRef.current);
123
+ }
124
+ };
125
+ }, [nodes, saveFlowConfig]);
126
+ return useMemo(() => ({ nodes, edges, onNodesChange, onEdgesChange, nodeTypes }), [nodes, edges, onNodesChange, onEdgesChange, nodeTypes]);
57
127
  };
@@ -1,9 +1,2 @@
1
- export type NodePosition = {
2
- [key: string]: {
3
- x: number;
4
- y: number;
5
- };
6
- };
7
- export declare const useSaveWorkflowConfig: (flowId: string) => {
8
- saveConfig: (body: NodePosition) => Promise<any>;
9
- };
1
+ import { FlowConfigResponse } from '@/views/flow/hooks/use-get-flow-state';
2
+ export declare const useSaveWorkflowConfig: () => (body: FlowConfigResponse) => Promise<any>;
@@ -1,13 +1,13 @@
1
1
  import { useCallback } from 'react';
2
- export const useSaveWorkflowConfig = (flowId) => {
3
- const saveConfig = useCallback(async (body) => {
2
+ export const useSaveWorkflowConfig = () => {
3
+ return useCallback(async (body) => {
4
4
  try {
5
- const response = await fetch(`/flows/${flowId}/config`, {
5
+ const response = await fetch(`/flows/${body.id}/config`, {
6
6
  method: 'POST',
7
7
  headers: {
8
8
  'Content-Type': 'application/json',
9
9
  },
10
- body: JSON.stringify({ [flowId]: body }),
10
+ body: JSON.stringify(body),
11
11
  });
12
12
  if (!response.ok) {
13
13
  throw new Error(`Failed to save config: ${response.statusText}`);
@@ -18,6 +18,5 @@ export const useSaveWorkflowConfig = (flowId) => {
18
18
  console.error('Error saving workflow config:', error);
19
19
  throw error;
20
20
  }
21
- }, [flowId]);
22
- return { saveConfig };
21
+ }, []);
23
22
  };
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Button } from '@/components/ui/button';
2
+ import { Button } from '@motiadev/ui';
3
3
  import { cn } from '@/lib/utils';
4
4
  import { LayoutList, X } from 'lucide-react';
5
5
  import { useState } from 'react';
@@ -1,6 +1,6 @@
1
1
  import { useNodesInitialized, useReactFlow } from '@xyflow/react';
2
2
  import dagre from 'dagre';
3
- import { useEffect } from 'react';
3
+ import { useEffect, useRef } from 'react';
4
4
  const organizeNodes = (nodes, edges) => {
5
5
  const dagreGraph = new dagre.graphlib.Graph({ compound: true });
6
6
  dagreGraph.setDefaultEdgeLabel(() => ({}));
@@ -37,8 +37,10 @@ const organizeNodes = (nodes, edges) => {
37
37
  export const NodeOrganizer = ({ onInitialized }) => {
38
38
  const { setNodes, getNodes, getEdges, fitView } = useReactFlow();
39
39
  const nodesInitialized = useNodesInitialized();
40
+ const initialized = useRef(false);
40
41
  useEffect(() => {
41
- if (nodesInitialized) {
42
+ if (nodesInitialized && !initialized.current) {
43
+ initialized.current = true;
42
44
  const nodes = getNodes();
43
45
  const edges = getEdges();
44
46
  const organizedNodes = organizeNodes(nodes, edges);