nvent 0.4.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.
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/dist/module.d.mts +193 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +974 -0
- package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +33 -0
- package/dist/runtime/app/components/ConfirmDialog.vue +121 -0
- package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +33 -0
- package/dist/runtime/app/components/FlowDiagram.d.vue.ts +64 -0
- package/dist/runtime/app/components/FlowDiagram.vue +338 -0
- package/dist/runtime/app/components/FlowDiagram.vue.d.ts +64 -0
- package/dist/runtime/app/components/FlowNodeCard.d.vue.ts +29 -0
- package/dist/runtime/app/components/FlowNodeCard.vue +156 -0
- package/dist/runtime/app/components/FlowNodeCard.vue.d.ts +29 -0
- package/dist/runtime/app/components/FlowRunOverview.d.vue.ts +9 -0
- package/dist/runtime/app/components/FlowRunOverview.vue +291 -0
- package/dist/runtime/app/components/FlowRunOverview.vue.d.ts +9 -0
- package/dist/runtime/app/components/FlowRunStatusBadge.d.vue.ts +14 -0
- package/dist/runtime/app/components/FlowRunStatusBadge.vue +60 -0
- package/dist/runtime/app/components/FlowRunStatusBadge.vue.d.ts +14 -0
- package/dist/runtime/app/components/FlowRunTimeline.d.vue.ts +12 -0
- package/dist/runtime/app/components/FlowRunTimeline.vue +127 -0
- package/dist/runtime/app/components/FlowRunTimeline.vue.d.ts +12 -0
- package/dist/runtime/app/components/FlowScheduleDialog.d.vue.ts +16 -0
- package/dist/runtime/app/components/FlowScheduleDialog.vue +226 -0
- package/dist/runtime/app/components/FlowScheduleDialog.vue.d.ts +16 -0
- package/dist/runtime/app/components/FlowSchedulesList.d.vue.ts +12 -0
- package/dist/runtime/app/components/FlowSchedulesList.vue +99 -0
- package/dist/runtime/app/components/FlowSchedulesList.vue.d.ts +12 -0
- package/dist/runtime/app/components/JobScheduling.d.vue.ts +6 -0
- package/dist/runtime/app/components/JobScheduling.vue +203 -0
- package/dist/runtime/app/components/JobScheduling.vue.d.ts +6 -0
- package/dist/runtime/app/components/ListItem.d.vue.ts +23 -0
- package/dist/runtime/app/components/ListItem.vue +70 -0
- package/dist/runtime/app/components/ListItem.vue.d.ts +23 -0
- package/dist/runtime/app/components/QueueConfigDetails.d.vue.ts +45 -0
- package/dist/runtime/app/components/QueueConfigDetails.vue +412 -0
- package/dist/runtime/app/components/QueueConfigDetails.vue.d.ts +45 -0
- package/dist/runtime/app/components/StatCounter.d.vue.ts +9 -0
- package/dist/runtime/app/components/StatCounter.vue +25 -0
- package/dist/runtime/app/components/StatCounter.vue.d.ts +9 -0
- package/dist/runtime/app/components/TimelineList.d.vue.ts +7 -0
- package/dist/runtime/app/components/TimelineList.vue +210 -0
- package/dist/runtime/app/components/TimelineList.vue.d.ts +7 -0
- package/dist/runtime/app/components/nhealth/component-router.d.vue.ts +46 -0
- package/dist/runtime/app/components/nhealth/component-router.vue +26 -0
- package/dist/runtime/app/components/nhealth/component-router.vue.d.ts +46 -0
- package/dist/runtime/app/components/nhealth/component-shell.d.vue.ts +24 -0
- package/dist/runtime/app/components/nhealth/component-shell.vue +89 -0
- package/dist/runtime/app/components/nhealth/component-shell.vue.d.ts +24 -0
- package/dist/runtime/app/composables/useAnalyzedFlows.d.ts +14 -0
- package/dist/runtime/app/composables/useAnalyzedFlows.js +7 -0
- package/dist/runtime/app/composables/useComponentRouter.d.ts +38 -0
- package/dist/runtime/app/composables/useComponentRouter.js +240 -0
- package/dist/runtime/app/composables/useFlowRunTimeline.d.ts +15 -0
- package/dist/runtime/app/composables/useFlowRunTimeline.js +66 -0
- package/dist/runtime/app/composables/useFlowRuns.d.ts +11 -0
- package/dist/runtime/app/composables/useFlowRuns.js +31 -0
- package/dist/runtime/app/composables/useFlowRunsInfinite.d.ts +24 -0
- package/dist/runtime/app/composables/useFlowRunsInfinite.js +123 -0
- package/dist/runtime/app/composables/useFlowRunsPolling.d.ts +8 -0
- package/dist/runtime/app/composables/useFlowRunsPolling.js +26 -0
- package/dist/runtime/app/composables/useFlowState.d.ts +125 -0
- package/dist/runtime/app/composables/useFlowState.js +211 -0
- package/dist/runtime/app/composables/useFlowWebSocket.d.ts +27 -0
- package/dist/runtime/app/composables/useFlowWebSocket.js +205 -0
- package/dist/runtime/app/composables/useFlowsNavigation.d.ts +10 -0
- package/dist/runtime/app/composables/useFlowsNavigation.js +57 -0
- package/dist/runtime/app/composables/useQueueJobs.d.ts +20 -0
- package/dist/runtime/app/composables/useQueueJobs.js +20 -0
- package/dist/runtime/app/composables/useQueueUpdates.d.ts +26 -0
- package/dist/runtime/app/composables/useQueueUpdates.js +122 -0
- package/dist/runtime/app/composables/useQueues.d.ts +43 -0
- package/dist/runtime/app/composables/useQueues.js +26 -0
- package/dist/runtime/app/composables/useQueuesLive.d.ts +19 -0
- package/dist/runtime/app/composables/useQueuesLive.js +143 -0
- package/dist/runtime/app/pages/flows/index.d.vue.ts +3 -0
- package/dist/runtime/app/pages/flows/index.vue +645 -0
- package/dist/runtime/app/pages/flows/index.vue.d.ts +3 -0
- package/dist/runtime/app/pages/index.d.vue.ts +3 -0
- package/dist/runtime/app/pages/index.vue +34 -0
- package/dist/runtime/app/pages/index.vue.d.ts +3 -0
- package/dist/runtime/app/pages/queues/index.d.vue.ts +3 -0
- package/dist/runtime/app/pages/queues/index.vue +229 -0
- package/dist/runtime/app/pages/queues/index.vue.d.ts +3 -0
- package/dist/runtime/app/pages/queues/job.d.vue.ts +3 -0
- package/dist/runtime/app/pages/queues/job.vue +262 -0
- package/dist/runtime/app/pages/queues/job.vue.d.ts +3 -0
- package/dist/runtime/app/pages/queues/jobs.d.vue.ts +3 -0
- package/dist/runtime/app/pages/queues/jobs.vue +291 -0
- package/dist/runtime/app/pages/queues/jobs.vue.d.ts +3 -0
- package/dist/runtime/app/plugins/vueflow.client.d.ts +6 -0
- package/dist/runtime/app/plugins/vueflow.client.js +15 -0
- package/dist/runtime/constants.d.ts +11 -0
- package/dist/runtime/constants.js +11 -0
- package/dist/runtime/python/get_config.py +64 -0
- package/dist/runtime/schema.d.ts +37 -0
- package/dist/runtime/schema.js +20 -0
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +10 -0
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +44 -0
- package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +7 -0
- package/dist/runtime/server/api/_flows/[name]/runs.get.js +53 -0
- package/dist/runtime/server/api/_flows/[name]/schedule.post.d.ts +2 -0
- package/dist/runtime/server/api/_flows/[name]/schedule.post.js +57 -0
- package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.d.ts +2 -0
- package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.js +42 -0
- package/dist/runtime/server/api/_flows/[name]/schedules.get.d.ts +2 -0
- package/dist/runtime/server/api/_flows/[name]/schedules.get.js +48 -0
- package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +2 -0
- package/dist/runtime/server/api/_flows/[name]/start.post.js +9 -0
- package/dist/runtime/server/api/_flows/index.get.d.ts +6 -0
- package/dist/runtime/server/api/_flows/index.get.js +5 -0
- package/dist/runtime/server/api/_flows/ws.d.ts +60 -0
- package/dist/runtime/server/api/_flows/ws.js +183 -0
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +9 -0
- package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/[name]/job/index.get.js +18 -0
- package/dist/runtime/server/api/_queues/index.get.d.ts +2 -0
- package/dist/runtime/server/api/_queues/index.get.js +63 -0
- package/dist/runtime/server/api/_queues/ws.d.ts +48 -0
- package/dist/runtime/server/api/_queues/ws.js +200 -0
- package/dist/runtime/server/events/adapters/fileAdapter.d.ts +2 -0
- package/dist/runtime/server/events/adapters/fileAdapter.js +382 -0
- package/dist/runtime/server/events/adapters/memoryAdapter.d.ts +2 -0
- package/dist/runtime/server/events/adapters/memoryAdapter.js +171 -0
- package/dist/runtime/server/events/adapters/redis/redisAdapter.d.ts +2 -0
- package/dist/runtime/server/events/adapters/redis/redisAdapter.js +348 -0
- package/dist/runtime/server/events/adapters/redis/redisPubSubGateway.d.ts +29 -0
- package/dist/runtime/server/events/adapters/redis/redisPubSubGateway.js +82 -0
- package/dist/runtime/server/events/eventBus.d.ts +20 -0
- package/dist/runtime/server/events/eventBus.js +35 -0
- package/dist/runtime/server/events/eventStoreFactory.d.ts +19 -0
- package/dist/runtime/server/events/eventStoreFactory.js +44 -0
- package/dist/runtime/server/events/streamNames.d.ts +17 -0
- package/dist/runtime/server/events/streamNames.js +17 -0
- package/dist/runtime/server/events/types.d.ts +63 -0
- package/dist/runtime/server/events/types.js +0 -0
- package/dist/runtime/server/events/wiring/flowWiring.d.ts +33 -0
- package/dist/runtime/server/events/wiring/flowWiring.js +406 -0
- package/dist/runtime/server/events/wiring/registry.d.ts +10 -0
- package/dist/runtime/server/events/wiring/registry.js +24 -0
- package/dist/runtime/server/plugins/00.event-store.d.ts +13 -0
- package/dist/runtime/server/plugins/00.event-store.js +16 -0
- package/dist/runtime/server/plugins/00.ws-lifecycle.d.ts +5 -0
- package/dist/runtime/server/plugins/00.ws-lifecycle.js +66 -0
- package/dist/runtime/server/plugins/flow-management.d.ts +13 -0
- package/dist/runtime/server/plugins/flow-management.js +65 -0
- package/dist/runtime/server/plugins/queue-management.d.ts +2 -0
- package/dist/runtime/server/plugins/queue-management.js +27 -0
- package/dist/runtime/server/plugins/state-cleanup.d.ts +11 -0
- package/dist/runtime/server/plugins/state-cleanup.js +93 -0
- package/dist/runtime/server/plugins/worker-management.d.ts +2 -0
- package/dist/runtime/server/plugins/worker-management.js +33 -0
- package/dist/runtime/server/queue/adapters/bullmq.d.ts +17 -0
- package/dist/runtime/server/queue/adapters/bullmq.js +164 -0
- package/dist/runtime/server/queue/queueFactory.d.ts +3 -0
- package/dist/runtime/server/queue/queueFactory.js +10 -0
- package/dist/runtime/server/queue/types.d.ts +47 -0
- package/dist/runtime/server/queue/types.js +0 -0
- package/dist/runtime/server/state/adapters/redis.d.ts +2 -0
- package/dist/runtime/server/state/adapters/redis.js +42 -0
- package/dist/runtime/server/state/stateFactory.d.ts +3 -0
- package/dist/runtime/server/state/stateFactory.js +17 -0
- package/dist/runtime/server/state/types.d.ts +23 -0
- package/dist/runtime/server/state/types.js +0 -0
- package/dist/runtime/server/tsconfig.json +3 -0
- package/dist/runtime/server/utils/defineQueueConfig.d.ts +154 -0
- package/dist/runtime/server/utils/defineQueueConfig.js +2 -0
- package/dist/runtime/server/utils/defineQueueWorker.d.ts +10 -0
- package/dist/runtime/server/utils/defineQueueWorker.js +17 -0
- package/dist/runtime/server/utils/useEventManager.d.ts +15 -0
- package/dist/runtime/server/utils/useEventManager.js +26 -0
- package/dist/runtime/server/utils/useEventStore.d.ts +20 -0
- package/dist/runtime/server/utils/useEventStore.js +119 -0
- package/dist/runtime/server/utils/useFlowEngine.d.ts +9 -0
- package/dist/runtime/server/utils/useFlowEngine.js +44 -0
- package/dist/runtime/server/utils/useLogs.d.ts +41 -0
- package/dist/runtime/server/utils/useLogs.js +74 -0
- package/dist/runtime/server/utils/useQueue.d.ts +31 -0
- package/dist/runtime/server/utils/useQueue.js +24 -0
- package/dist/runtime/server/utils/useServerLogger.d.ts +42 -0
- package/dist/runtime/server/utils/useServerLogger.js +54 -0
- package/dist/runtime/server/utils/wsPeerManager.d.ts +34 -0
- package/dist/runtime/server/utils/wsPeerManager.js +23 -0
- package/dist/runtime/server/worker/adapter.d.ts +4 -0
- package/dist/runtime/server/worker/adapter.js +65 -0
- package/dist/runtime/server/worker/runner/node.d.ts +27 -0
- package/dist/runtime/server/worker/runner/node.js +196 -0
- package/dist/runtime/types.d.ts +132 -0
- package/dist/types.d.mts +3 -0
- package/package.json +75 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { ref, computed } from "vue";
|
|
2
|
+
export function reduceFlowState(events) {
|
|
3
|
+
const state = {
|
|
4
|
+
status: "running",
|
|
5
|
+
steps: {},
|
|
6
|
+
logs: []
|
|
7
|
+
};
|
|
8
|
+
for (const e of events) {
|
|
9
|
+
const eventType = e.type;
|
|
10
|
+
const stepKey = e.stepName;
|
|
11
|
+
switch (eventType) {
|
|
12
|
+
case "flow.start":
|
|
13
|
+
case "flow.started":
|
|
14
|
+
state.status = "running";
|
|
15
|
+
state.startedAt = e.ts;
|
|
16
|
+
if (e.flowName) state.meta = { ...state.meta, flowName: e.flowName };
|
|
17
|
+
if (e.data?.flowName) state.meta = { ...state.meta, flowName: e.data.flowName };
|
|
18
|
+
if (e.data?.input) state.meta = { ...state.meta, input: e.data.input };
|
|
19
|
+
break;
|
|
20
|
+
case "flow.complete":
|
|
21
|
+
case "flow.completed":
|
|
22
|
+
state.status = "completed";
|
|
23
|
+
state.completedAt = e.ts;
|
|
24
|
+
if (e.data?.result) state.meta = { ...state.meta, result: e.data.result };
|
|
25
|
+
break;
|
|
26
|
+
case "flow.failed":
|
|
27
|
+
state.status = "failed";
|
|
28
|
+
state.completedAt = e.ts;
|
|
29
|
+
if (e.data?.error) state.meta = { ...state.meta, error: e.data.error };
|
|
30
|
+
break;
|
|
31
|
+
case "step.started": {
|
|
32
|
+
if (!stepKey) break;
|
|
33
|
+
if (!state.steps[stepKey]) {
|
|
34
|
+
state.steps[stepKey] = {
|
|
35
|
+
status: "running",
|
|
36
|
+
attempt: 1
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const currentStatus = state.steps[stepKey].status;
|
|
40
|
+
if (currentStatus !== "completed" && currentStatus !== "failed" && currentStatus !== "timeout") {
|
|
41
|
+
state.steps[stepKey].status = "running";
|
|
42
|
+
}
|
|
43
|
+
state.steps[stepKey].startedAt = e.ts;
|
|
44
|
+
state.steps[stepKey].attempt = e.attempt || state.steps[stepKey].attempt || 1;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
case "step.completed": {
|
|
48
|
+
if (!stepKey) break;
|
|
49
|
+
if (!state.steps[stepKey]) {
|
|
50
|
+
state.steps[stepKey] = { status: "completed", attempt: 1 };
|
|
51
|
+
}
|
|
52
|
+
state.steps[stepKey].status = "completed";
|
|
53
|
+
state.steps[stepKey].completedAt = e.ts;
|
|
54
|
+
if (e.data?.result !== void 0) state.steps[stepKey].result = e.data.result;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "step.failed": {
|
|
58
|
+
if (!stepKey) break;
|
|
59
|
+
if (!state.steps[stepKey]) {
|
|
60
|
+
state.steps[stepKey] = { status: "failed", attempt: 1 };
|
|
61
|
+
}
|
|
62
|
+
const willRetry = e.data?.willRetry || e.data?.retry;
|
|
63
|
+
state.steps[stepKey].status = willRetry ? "retrying" : "failed";
|
|
64
|
+
state.steps[stepKey].error = e.data?.error || e.data?.message;
|
|
65
|
+
if (!willRetry) {
|
|
66
|
+
state.steps[stepKey].completedAt = e.ts;
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case "step.retry": {
|
|
71
|
+
if (!stepKey) break;
|
|
72
|
+
if (!state.steps[stepKey]) {
|
|
73
|
+
state.steps[stepKey] = { status: "retrying", attempt: 1 };
|
|
74
|
+
}
|
|
75
|
+
state.steps[stepKey].status = "retrying";
|
|
76
|
+
state.steps[stepKey].attempt = e.data?.nextAttempt || e.attempt || 1;
|
|
77
|
+
state.steps[stepKey].error = e.data?.error;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case "step.await.time":
|
|
81
|
+
case "step.await.event":
|
|
82
|
+
case "step.await.trigger": {
|
|
83
|
+
if (!stepKey) break;
|
|
84
|
+
if (!state.steps[stepKey]) {
|
|
85
|
+
state.steps[stepKey] = { status: "waiting", attempt: 1 };
|
|
86
|
+
}
|
|
87
|
+
state.steps[stepKey].status = "waiting";
|
|
88
|
+
state.steps[stepKey].awaitType = eventType.split(".")[2];
|
|
89
|
+
state.steps[stepKey].awaitData = e.data;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case "step.resumed": {
|
|
93
|
+
if (!stepKey) break;
|
|
94
|
+
if (!state.steps[stepKey]) {
|
|
95
|
+
state.steps[stepKey] = { status: "running", attempt: 1 };
|
|
96
|
+
}
|
|
97
|
+
state.steps[stepKey].status = "running";
|
|
98
|
+
delete state.steps[stepKey].awaitType;
|
|
99
|
+
delete state.steps[stepKey].awaitData;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case "step.await.timeout": {
|
|
103
|
+
if (!stepKey) break;
|
|
104
|
+
if (!state.steps[stepKey]) {
|
|
105
|
+
state.steps[stepKey] = { status: "timeout", attempt: 1 };
|
|
106
|
+
}
|
|
107
|
+
state.steps[stepKey].status = "timeout";
|
|
108
|
+
state.steps[stepKey].error = `Await timeout after ${e.data?.duration}ms`;
|
|
109
|
+
state.steps[stepKey].completedAt = e.ts;
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case "runner.log":
|
|
113
|
+
case "log": {
|
|
114
|
+
state.logs.push({
|
|
115
|
+
ts: e.ts,
|
|
116
|
+
step: stepKey,
|
|
117
|
+
level: e.data?.level || "info",
|
|
118
|
+
msg: e.data?.message || e.data?.msg || (typeof e.data === "string" ? e.data : String(e.data)),
|
|
119
|
+
data: e.data
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
// Handle any unrecognized events - log for debugging
|
|
124
|
+
default: {
|
|
125
|
+
if (typeof console !== "undefined" && eventType && !eventType.startsWith("_")) {
|
|
126
|
+
console.debug("[useFlowState] Unhandled event type:", eventType, {
|
|
127
|
+
stepName: e.stepName,
|
|
128
|
+
stepKey,
|
|
129
|
+
data: e.data
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (!state.startedAt && events.length > 0 && events[0]) {
|
|
136
|
+
state.startedAt = events[0].ts;
|
|
137
|
+
}
|
|
138
|
+
if (state.status === "running" && state.startedAt && Object.keys(state.steps).length > 0) {
|
|
139
|
+
const hasRunningSteps = Object.values(state.steps).some(
|
|
140
|
+
(s) => s.status === "running" || s.status === "retrying" || s.status === "waiting"
|
|
141
|
+
);
|
|
142
|
+
const hasFailedSteps = Object.values(state.steps).some((s) => s.status === "failed");
|
|
143
|
+
const allStepsTerminal = Object.values(state.steps).every(
|
|
144
|
+
(s) => s.status === "completed" || s.status === "failed" || s.status === "timeout"
|
|
145
|
+
);
|
|
146
|
+
if (!hasRunningSteps && allStepsTerminal) {
|
|
147
|
+
if (hasFailedSteps) {
|
|
148
|
+
state.status = "failed";
|
|
149
|
+
} else {
|
|
150
|
+
state.status = "completed";
|
|
151
|
+
}
|
|
152
|
+
const latestCompletion = Object.values(state.steps).map((s) => s.completedAt).filter(Boolean).sort().pop();
|
|
153
|
+
if (latestCompletion) {
|
|
154
|
+
state.completedAt = latestCompletion;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return state;
|
|
159
|
+
}
|
|
160
|
+
export function useFlowState(initialEvents = []) {
|
|
161
|
+
const events = ref(initialEvents);
|
|
162
|
+
const state = computed(() => reduceFlowState(events.value));
|
|
163
|
+
const addEvent = (event) => {
|
|
164
|
+
events.value.push(event);
|
|
165
|
+
};
|
|
166
|
+
const addEvents = (newEvents) => {
|
|
167
|
+
events.value.push(...newEvents);
|
|
168
|
+
};
|
|
169
|
+
const reset = (newEvents = []) => {
|
|
170
|
+
events.value = newEvents;
|
|
171
|
+
};
|
|
172
|
+
const isRunning = computed(() => state.value.status === "running");
|
|
173
|
+
const isCompleted = computed(() => state.value.status === "completed");
|
|
174
|
+
const isFailed = computed(() => state.value.status === "failed");
|
|
175
|
+
const stepList = computed(() => {
|
|
176
|
+
return Object.entries(state.value.steps).map(([key, step]) => ({
|
|
177
|
+
key,
|
|
178
|
+
...step
|
|
179
|
+
}));
|
|
180
|
+
});
|
|
181
|
+
const runningSteps = computed(() => {
|
|
182
|
+
return stepList.value.filter((s) => s.status === "running");
|
|
183
|
+
});
|
|
184
|
+
const waitingSteps = computed(() => {
|
|
185
|
+
return stepList.value.filter((s) => s.status === "waiting");
|
|
186
|
+
});
|
|
187
|
+
const failedSteps = computed(() => {
|
|
188
|
+
return stepList.value.filter((s) => s.status === "failed");
|
|
189
|
+
});
|
|
190
|
+
const completedSteps = computed(() => {
|
|
191
|
+
return stepList.value.filter((s) => s.status === "completed");
|
|
192
|
+
});
|
|
193
|
+
return {
|
|
194
|
+
// Raw data
|
|
195
|
+
events,
|
|
196
|
+
state,
|
|
197
|
+
// Methods
|
|
198
|
+
addEvent,
|
|
199
|
+
addEvents,
|
|
200
|
+
reset,
|
|
201
|
+
// Computed helpers
|
|
202
|
+
isRunning,
|
|
203
|
+
isCompleted,
|
|
204
|
+
isFailed,
|
|
205
|
+
stepList,
|
|
206
|
+
runningSteps,
|
|
207
|
+
waitingSteps,
|
|
208
|
+
failedSteps,
|
|
209
|
+
completedSteps
|
|
210
|
+
};
|
|
211
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface UseFlowWebSocketOptions {
|
|
2
|
+
autoReconnect?: boolean;
|
|
3
|
+
maxRetries?: number;
|
|
4
|
+
baseDelayMs?: number;
|
|
5
|
+
maxDelayMs?: number;
|
|
6
|
+
onOpen?: () => void;
|
|
7
|
+
onError?: (err?: any) => void;
|
|
8
|
+
onClose?: (event?: CloseEvent) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface FlowSubscription {
|
|
11
|
+
flowName: string;
|
|
12
|
+
runId: string;
|
|
13
|
+
onEvent: (event: any) => void;
|
|
14
|
+
onHistory?: (events: any[]) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* WebSocket composable for flow run events
|
|
18
|
+
* Replaces the SSE-based useEventSSE with a more reliable WebSocket implementation
|
|
19
|
+
*/
|
|
20
|
+
export declare function useFlowWebSocket(): {
|
|
21
|
+
subscribe: (subscription: FlowSubscription, opts?: UseFlowWebSocketOptions) => void;
|
|
22
|
+
unsubscribe: () => void;
|
|
23
|
+
stop: () => void;
|
|
24
|
+
connected: import("vue").Ref<boolean, boolean>;
|
|
25
|
+
reconnecting: import("vue").Ref<boolean, boolean>;
|
|
26
|
+
};
|
|
27
|
+
export default useFlowWebSocket;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { ref, onBeforeUnmount } from "#imports";
|
|
2
|
+
export function useFlowWebSocket() {
|
|
3
|
+
const ws = ref(null);
|
|
4
|
+
const connected = ref(false);
|
|
5
|
+
const reconnecting = ref(false);
|
|
6
|
+
let retry = 0;
|
|
7
|
+
let reconnectTimer = null;
|
|
8
|
+
let currentOptions;
|
|
9
|
+
let currentSubscription = null;
|
|
10
|
+
let pingInterval = null;
|
|
11
|
+
let isServerRestarting = false;
|
|
12
|
+
const computeDelay = (opts) => {
|
|
13
|
+
const base = Math.max(100, opts?.baseDelayMs ?? 1e3);
|
|
14
|
+
const max = Math.max(base, opts?.maxDelayMs ?? 1e4);
|
|
15
|
+
const exp = Math.min(max, base * Math.pow(2, retry));
|
|
16
|
+
const jitter = Math.floor(Math.random() * Math.min(1e3, exp / 4));
|
|
17
|
+
return exp + jitter;
|
|
18
|
+
};
|
|
19
|
+
const clearTimers = () => {
|
|
20
|
+
if (reconnectTimer) {
|
|
21
|
+
try {
|
|
22
|
+
clearTimeout(reconnectTimer);
|
|
23
|
+
} catch {
|
|
24
|
+
}
|
|
25
|
+
reconnectTimer = null;
|
|
26
|
+
}
|
|
27
|
+
if (pingInterval) {
|
|
28
|
+
try {
|
|
29
|
+
clearInterval(pingInterval);
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
pingInterval = null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const send = (data) => {
|
|
36
|
+
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
|
|
37
|
+
ws.value.send(JSON.stringify(data));
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const startPingInterval = () => {
|
|
41
|
+
clearTimers();
|
|
42
|
+
pingInterval = setInterval(() => {
|
|
43
|
+
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
|
|
44
|
+
send({ type: "ping" });
|
|
45
|
+
}
|
|
46
|
+
}, 3e4);
|
|
47
|
+
};
|
|
48
|
+
const stop = () => {
|
|
49
|
+
clearTimers();
|
|
50
|
+
isServerRestarting = false;
|
|
51
|
+
try {
|
|
52
|
+
if (ws.value) {
|
|
53
|
+
ws.value.close(1e3, "Client closing");
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.warn("[useFlowWebSocket] Error closing WebSocket:", err);
|
|
57
|
+
}
|
|
58
|
+
ws.value = null;
|
|
59
|
+
connected.value = false;
|
|
60
|
+
reconnecting.value = false;
|
|
61
|
+
retry = 0;
|
|
62
|
+
currentSubscription = null;
|
|
63
|
+
};
|
|
64
|
+
const attemptReconnect = () => {
|
|
65
|
+
if (!currentOptions?.autoReconnect) {
|
|
66
|
+
stop();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const max = Math.max(0, currentOptions?.maxRetries ?? 10);
|
|
70
|
+
if (retry >= max) {
|
|
71
|
+
console.error("[useFlowWebSocket] Max retries reached");
|
|
72
|
+
stop();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
retry++;
|
|
76
|
+
reconnecting.value = true;
|
|
77
|
+
const baseDelay = isServerRestarting ? 2e3 : computeDelay(currentOptions);
|
|
78
|
+
const delay = baseDelay;
|
|
79
|
+
console.log(`[useFlowWebSocket] Will attempt reconnection in ${delay}ms (attempt ${retry}/${max})${isServerRestarting ? " [server restart]" : ""}`);
|
|
80
|
+
clearTimers();
|
|
81
|
+
reconnectTimer = setTimeout(() => {
|
|
82
|
+
if (currentSubscription) {
|
|
83
|
+
innerSubscribe(currentSubscription, currentOptions);
|
|
84
|
+
}
|
|
85
|
+
}, delay);
|
|
86
|
+
};
|
|
87
|
+
const setupWebSocket = (socket, subscription, opts) => {
|
|
88
|
+
socket.onopen = () => {
|
|
89
|
+
console.log("[useFlowWebSocket] Connected");
|
|
90
|
+
connected.value = true;
|
|
91
|
+
reconnecting.value = false;
|
|
92
|
+
retry = 0;
|
|
93
|
+
startPingInterval();
|
|
94
|
+
send({
|
|
95
|
+
type: "subscribe",
|
|
96
|
+
flowName: subscription.flowName,
|
|
97
|
+
runId: subscription.runId
|
|
98
|
+
});
|
|
99
|
+
opts?.onOpen?.();
|
|
100
|
+
};
|
|
101
|
+
socket.onmessage = (event) => {
|
|
102
|
+
try {
|
|
103
|
+
const data = JSON.parse(event.data);
|
|
104
|
+
switch (data.type) {
|
|
105
|
+
case "connected":
|
|
106
|
+
console.log("[useFlowWebSocket] Server acknowledged connection");
|
|
107
|
+
break;
|
|
108
|
+
case "subscribed":
|
|
109
|
+
console.log("[useFlowWebSocket] Subscribed to flow:", data.flowName, data.runId);
|
|
110
|
+
break;
|
|
111
|
+
case "unsubscribed":
|
|
112
|
+
console.log("[useFlowWebSocket] Unsubscribed from flow:", data.flowName, data.runId);
|
|
113
|
+
break;
|
|
114
|
+
case "history":
|
|
115
|
+
if (subscription.onHistory) {
|
|
116
|
+
subscription.onHistory(data.events);
|
|
117
|
+
} else {
|
|
118
|
+
for (const eventData of data.events) {
|
|
119
|
+
subscription.onEvent(eventData);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case "event":
|
|
124
|
+
subscription.onEvent(data.event);
|
|
125
|
+
break;
|
|
126
|
+
case "pong":
|
|
127
|
+
break;
|
|
128
|
+
case "server-restart":
|
|
129
|
+
console.log("[useFlowWebSocket] Server is restarting (HMR)");
|
|
130
|
+
isServerRestarting = true;
|
|
131
|
+
break;
|
|
132
|
+
case "error":
|
|
133
|
+
console.error("[useFlowWebSocket] Server error:", data.message);
|
|
134
|
+
opts?.onError?.(new Error(data.message));
|
|
135
|
+
break;
|
|
136
|
+
default:
|
|
137
|
+
console.warn("[useFlowWebSocket] Unknown message type:", data.type);
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.error("[useFlowWebSocket] Error parsing message:", err);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
socket.onerror = (err) => {
|
|
144
|
+
console.error("[useFlowWebSocket] WebSocket error:", err);
|
|
145
|
+
opts?.onError?.(err);
|
|
146
|
+
};
|
|
147
|
+
socket.onclose = (event) => {
|
|
148
|
+
console.log("[useFlowWebSocket] Connection closed:", event.code, event.reason);
|
|
149
|
+
connected.value = false;
|
|
150
|
+
clearTimers();
|
|
151
|
+
opts?.onClose?.(event);
|
|
152
|
+
const shouldReconnect = event.code !== 1e3 && opts?.autoReconnect;
|
|
153
|
+
if (shouldReconnect) {
|
|
154
|
+
console.log("[useFlowWebSocket] Will attempt reconnection (code:", event.code, ")");
|
|
155
|
+
attemptReconnect();
|
|
156
|
+
} else {
|
|
157
|
+
isServerRestarting = false;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
const innerSubscribe = (subscription, opts) => {
|
|
162
|
+
if (import.meta.server || typeof WebSocket === "undefined") {
|
|
163
|
+
console.warn("[useFlowWebSocket] WebSocket not available (SSR context)");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (ws.value) {
|
|
167
|
+
stop();
|
|
168
|
+
}
|
|
169
|
+
currentOptions = opts;
|
|
170
|
+
currentSubscription = subscription;
|
|
171
|
+
try {
|
|
172
|
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
173
|
+
const wsUrl = `${protocol}//${window.location.host}/api/_flows/ws`;
|
|
174
|
+
const socket = new WebSocket(wsUrl);
|
|
175
|
+
ws.value = socket;
|
|
176
|
+
setupWebSocket(socket, subscription, opts);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.error("[useFlowWebSocket] Error creating WebSocket:", err);
|
|
179
|
+
opts?.onError?.(err);
|
|
180
|
+
attemptReconnect();
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const subscribe = (subscription, opts) => {
|
|
184
|
+
innerSubscribe(subscription, opts);
|
|
185
|
+
};
|
|
186
|
+
const unsubscribe = () => {
|
|
187
|
+
if (currentSubscription && ws.value && ws.value.readyState === WebSocket.OPEN) {
|
|
188
|
+
send({
|
|
189
|
+
type: "unsubscribe",
|
|
190
|
+
flowName: currentSubscription.flowName,
|
|
191
|
+
runId: currentSubscription.runId
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
currentSubscription = null;
|
|
195
|
+
};
|
|
196
|
+
onBeforeUnmount(() => stop());
|
|
197
|
+
return {
|
|
198
|
+
subscribe,
|
|
199
|
+
unsubscribe,
|
|
200
|
+
stop,
|
|
201
|
+
connected,
|
|
202
|
+
reconnecting
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export default useFlowWebSocket;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export function useFlowsNavigation() {
|
|
2
|
+
const route = useRoute();
|
|
3
|
+
const router = useRouter();
|
|
4
|
+
const selectedFlow = computed({
|
|
5
|
+
get: () => route.query.flow || "",
|
|
6
|
+
set: (value) => {
|
|
7
|
+
router.push({
|
|
8
|
+
query: {
|
|
9
|
+
...route.query,
|
|
10
|
+
flow: value || void 0,
|
|
11
|
+
run: void 0
|
|
12
|
+
// Clear run when flow changes
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
const selectedRunId = computed({
|
|
18
|
+
get: () => route.query.run || "",
|
|
19
|
+
set: (value) => {
|
|
20
|
+
router.push({
|
|
21
|
+
query: {
|
|
22
|
+
...route.query,
|
|
23
|
+
run: value || void 0
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const timelineOpen = ref(route.query.timeline === "true");
|
|
29
|
+
watch(timelineOpen, (value) => {
|
|
30
|
+
router.push({
|
|
31
|
+
query: {
|
|
32
|
+
...route.query,
|
|
33
|
+
timeline: value ? "true" : void 0
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
watch(() => route.query.timeline, (value) => {
|
|
38
|
+
timelineOpen.value = value === "true";
|
|
39
|
+
});
|
|
40
|
+
const selectedTab = computed({
|
|
41
|
+
get: () => route.query.tab || "overview",
|
|
42
|
+
set: (value) => {
|
|
43
|
+
router.push({
|
|
44
|
+
query: {
|
|
45
|
+
...route.query,
|
|
46
|
+
tab: value !== "overview" ? value : void 0
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
selectedFlow,
|
|
53
|
+
selectedRunId,
|
|
54
|
+
timelineOpen,
|
|
55
|
+
selectedTab
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Ref } from '#imports';
|
|
2
|
+
export interface Job {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
data: any;
|
|
6
|
+
state?: 'waiting' | 'active' | 'completed' | 'failed' | 'delayed' | 'paused';
|
|
7
|
+
returnvalue?: any;
|
|
8
|
+
failedReason?: string;
|
|
9
|
+
timestamp?: number;
|
|
10
|
+
processedOn?: number;
|
|
11
|
+
finishedOn?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface JobsResponse {
|
|
14
|
+
jobs: Job[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Composable for fetching jobs for a queue
|
|
18
|
+
* Client-only to avoid hydration mismatches
|
|
19
|
+
*/
|
|
20
|
+
export declare function useQueueJobs(queueName: Ref<string>, state?: Ref<string | null>): any;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ref } from "#imports";
|
|
2
|
+
export function useQueueJobs(queueName, state = ref(null)) {
|
|
3
|
+
return useFetch(
|
|
4
|
+
() => {
|
|
5
|
+
const params = new URLSearchParams();
|
|
6
|
+
if (state.value) {
|
|
7
|
+
params.append("state", state.value);
|
|
8
|
+
}
|
|
9
|
+
const queryString = params.toString();
|
|
10
|
+
return `/api/_queues/${encodeURIComponent(queueName.value)}/job${queryString ? `?${queryString}` : ""}`;
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
key: () => `queue-jobs-${queueName.value}-${state.value || "all"}`,
|
|
14
|
+
watch: [queueName, state],
|
|
15
|
+
immediate: true,
|
|
16
|
+
server: false
|
|
17
|
+
// Client-only
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Ref } from '#imports';
|
|
2
|
+
/**
|
|
3
|
+
* Composable for real-time queue updates via WebSocket
|
|
4
|
+
*/
|
|
5
|
+
export declare function useQueueUpdates(queueName: Ref<string>): {
|
|
6
|
+
isConnected: import("vue").ComputedRef<boolean>;
|
|
7
|
+
isReconnecting: import("vue").ComputedRef<boolean>;
|
|
8
|
+
counts: import("vue").ComputedRef<{
|
|
9
|
+
active: number;
|
|
10
|
+
completed: number;
|
|
11
|
+
failed: number;
|
|
12
|
+
delayed: number;
|
|
13
|
+
waiting: number;
|
|
14
|
+
paused: number;
|
|
15
|
+
} | null>;
|
|
16
|
+
lastEvent: import("vue").ComputedRef<{
|
|
17
|
+
[x: string]: any;
|
|
18
|
+
eventType: "waiting" | "active" | "completed" | "failed" | "progress";
|
|
19
|
+
jobId?: string | undefined;
|
|
20
|
+
} | null>;
|
|
21
|
+
countsUpdatedAt: import("vue").ComputedRef<number | null>;
|
|
22
|
+
shouldRefreshJobs: import("vue").ComputedRef<boolean>;
|
|
23
|
+
resetRefreshFlag: () => void;
|
|
24
|
+
connect: () => void;
|
|
25
|
+
disconnect: () => void;
|
|
26
|
+
};
|