nvent 0.4.4 → 0.5.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/dist/module.d.mts +4 -185
- package/dist/module.json +3 -3
- package/dist/module.mjs +451 -257
- package/dist/runtime/adapters/base/index.d.ts +6 -0
- package/dist/runtime/adapters/base/index.js +1 -0
- package/dist/runtime/adapters/base/store-validator.d.ts +48 -0
- package/dist/runtime/adapters/base/store-validator.js +147 -0
- package/dist/runtime/adapters/builtin/file-queue.d.ts +67 -0
- package/dist/runtime/adapters/builtin/file-queue.js +499 -0
- package/dist/runtime/adapters/builtin/file-store.d.ts +32 -0
- package/dist/runtime/adapters/builtin/file-store.js +206 -0
- package/dist/runtime/adapters/builtin/file-stream.d.ts +39 -0
- package/dist/runtime/adapters/builtin/file-stream.js +56 -0
- package/dist/runtime/adapters/builtin/index.d.ts +10 -0
- package/dist/runtime/adapters/builtin/index.js +5 -0
- package/dist/runtime/adapters/builtin/memory-queue.d.ts +52 -0
- package/dist/runtime/adapters/builtin/memory-queue.js +243 -0
- package/dist/runtime/adapters/builtin/memory-store.d.ts +68 -0
- package/dist/runtime/adapters/builtin/memory-store.js +333 -0
- package/dist/runtime/adapters/builtin/memory-stream.d.ts +21 -0
- package/dist/runtime/adapters/builtin/memory-stream.js +56 -0
- package/dist/runtime/adapters/factory.d.ts +31 -0
- package/dist/runtime/adapters/factory.js +134 -0
- package/dist/runtime/adapters/index.d.ts +8 -0
- package/dist/runtime/adapters/index.js +3 -0
- package/dist/runtime/adapters/interfaces/index.d.ts +11 -0
- package/dist/runtime/adapters/interfaces/index.js +3 -0
- package/dist/runtime/adapters/interfaces/queue.d.ts +150 -0
- package/dist/runtime/adapters/interfaces/store.d.ts +297 -0
- package/dist/runtime/adapters/interfaces/stream.d.ts +62 -0
- package/dist/runtime/adapters/registry.d.ts +85 -0
- package/dist/runtime/adapters/registry.js +161 -0
- package/dist/runtime/config/index.d.ts +29 -0
- package/dist/runtime/config/index.js +175 -0
- package/dist/runtime/config/types.d.ts +397 -0
- package/dist/runtime/config/types.js +0 -0
- package/dist/runtime/{server-utils/events → events}/eventBus.d.ts +1 -1
- package/dist/runtime/events/types.d.ts +145 -0
- package/dist/runtime/events/types.js +0 -0
- package/dist/runtime/events/utils/scheduleTrigger.d.ts +8 -0
- package/dist/runtime/events/utils/scheduleTrigger.js +69 -0
- package/dist/runtime/events/utils/stallDetector.d.ts +140 -0
- package/dist/runtime/events/utils/stallDetector.js +436 -0
- package/dist/runtime/events/utils/triggerRuntime.d.ts +58 -0
- package/dist/runtime/events/utils/triggerRuntime.js +212 -0
- package/dist/runtime/{server-utils/events → events}/wiring/flowWiring.d.ts +12 -11
- package/dist/runtime/events/wiring/flowWiring.js +1020 -0
- package/dist/runtime/events/wiring/registry.d.ts +19 -0
- package/dist/runtime/events/wiring/registry.js +35 -0
- package/dist/runtime/events/wiring/stateWiring.d.ts +37 -0
- package/dist/runtime/events/wiring/stateWiring.js +92 -0
- package/dist/runtime/events/wiring/streamWiring.d.ts +36 -0
- package/dist/runtime/events/wiring/streamWiring.js +156 -0
- package/dist/runtime/events/wiring/triggerWiring.d.ts +21 -0
- package/dist/runtime/events/wiring/triggerWiring.js +412 -0
- package/dist/runtime/nitro/plugins/00.adapters.d.ts +14 -0
- package/dist/runtime/nitro/plugins/00.adapters.js +73 -0
- package/dist/runtime/nitro/plugins/02.workers.js +63 -0
- package/dist/runtime/nitro/plugins/03.triggers.d.ts +12 -0
- package/dist/runtime/nitro/plugins/03.triggers.js +55 -0
- package/dist/runtime/nitro/routes/webhook.await.d.ts +23 -0
- package/dist/runtime/nitro/routes/webhook.await.js +90 -0
- package/dist/runtime/nitro/routes/webhook.trigger.d.ts +69 -0
- package/dist/runtime/nitro/routes/webhook.trigger.js +64 -0
- package/dist/runtime/nitro/utils/adapters.d.ts +66 -0
- package/dist/runtime/nitro/utils/adapters.js +51 -0
- package/dist/runtime/nitro/utils/awaitPatterns/event.d.ts +15 -0
- package/dist/runtime/nitro/utils/awaitPatterns/event.js +120 -0
- package/dist/runtime/nitro/utils/awaitPatterns/index.d.ts +28 -0
- package/dist/runtime/nitro/utils/awaitPatterns/index.js +55 -0
- package/dist/runtime/nitro/utils/awaitPatterns/schedule.d.ts +16 -0
- package/dist/runtime/nitro/utils/awaitPatterns/schedule.js +78 -0
- package/dist/runtime/nitro/utils/awaitPatterns/time.d.ts +15 -0
- package/dist/runtime/nitro/utils/awaitPatterns/time.js +67 -0
- package/dist/runtime/nitro/utils/awaitPatterns/webhook.d.ts +15 -0
- package/dist/runtime/nitro/utils/awaitPatterns/webhook.js +120 -0
- package/dist/runtime/nitro/utils/defineFunction.d.ts +10 -0
- package/dist/runtime/nitro/utils/defineFunction.js +17 -0
- package/dist/runtime/nitro/utils/defineFunctionConfig.d.ts +310 -0
- package/dist/runtime/nitro/utils/defineFunctionConfig.js +3 -0
- package/dist/runtime/nitro/utils/defineHooks.d.ts +41 -0
- package/dist/runtime/nitro/utils/defineHooks.js +6 -0
- package/dist/runtime/nitro/utils/registerAdapter.d.ts +59 -0
- package/dist/runtime/nitro/utils/registerAdapter.js +13 -0
- package/dist/runtime/nitro/utils/useAwait.d.ts +71 -0
- package/dist/runtime/nitro/utils/useAwait.js +139 -0
- package/dist/runtime/{server-utils → nitro}/utils/useEventManager.d.ts +2 -2
- package/dist/runtime/{server-utils → nitro}/utils/useEventManager.js +1 -1
- package/dist/runtime/nitro/utils/useFlow.d.ts +68 -0
- package/dist/runtime/nitro/utils/useFlow.js +226 -0
- package/dist/runtime/nitro/utils/useHookRegistry.d.ts +34 -0
- package/dist/runtime/nitro/utils/useHookRegistry.js +25 -0
- package/dist/runtime/{server-utils → nitro}/utils/useNventLogger.js +2 -2
- package/dist/runtime/nitro/utils/useRunContext.d.ts +6 -0
- package/dist/runtime/nitro/utils/useRunContext.js +102 -0
- package/dist/runtime/nitro/utils/useStreamTopics.d.ts +83 -0
- package/dist/runtime/nitro/utils/useStreamTopics.js +94 -0
- package/dist/runtime/nitro/utils/useTrigger.d.ts +150 -0
- package/dist/runtime/nitro/utils/useTrigger.js +320 -0
- package/dist/runtime/scheduler/index.d.ts +33 -0
- package/dist/runtime/scheduler/index.js +38 -0
- package/dist/runtime/scheduler/scheduler.d.ts +113 -0
- package/dist/runtime/scheduler/scheduler.js +623 -0
- package/dist/runtime/scheduler/types.d.ts +116 -0
- package/dist/runtime/scheduler/types.js +0 -0
- package/dist/runtime/tsconfig.json +8 -0
- package/dist/runtime/worker/node/runner.d.ts +53 -0
- package/dist/runtime/worker/node/runner.js +327 -0
- package/dist/types.d.mts +2 -2
- package/package.json +16 -46
- package/LICENSE +0 -21
- package/README.md +0 -389
- package/dist/runtime/app/assets/vueflow.css +0 -1
- package/dist/runtime/app/components/ConfirmDialog.d.vue.ts +0 -33
- package/dist/runtime/app/components/ConfirmDialog.vue +0 -121
- package/dist/runtime/app/components/ConfirmDialog.vue.d.ts +0 -33
- package/dist/runtime/app/components/FlowDiagram.d.vue.ts +0 -64
- package/dist/runtime/app/components/FlowDiagram.vue +0 -338
- package/dist/runtime/app/components/FlowDiagram.vue.d.ts +0 -64
- package/dist/runtime/app/components/FlowNodeCard.d.vue.ts +0 -29
- package/dist/runtime/app/components/FlowNodeCard.vue +0 -156
- package/dist/runtime/app/components/FlowNodeCard.vue.d.ts +0 -29
- package/dist/runtime/app/components/FlowRunOverview.d.vue.ts +0 -9
- package/dist/runtime/app/components/FlowRunOverview.vue +0 -291
- package/dist/runtime/app/components/FlowRunOverview.vue.d.ts +0 -9
- package/dist/runtime/app/components/FlowRunStatusBadge.d.vue.ts +0 -14
- package/dist/runtime/app/components/FlowRunStatusBadge.vue +0 -60
- package/dist/runtime/app/components/FlowRunStatusBadge.vue.d.ts +0 -14
- package/dist/runtime/app/components/FlowRunTimeline.d.vue.ts +0 -12
- package/dist/runtime/app/components/FlowRunTimeline.vue +0 -127
- package/dist/runtime/app/components/FlowRunTimeline.vue.d.ts +0 -12
- package/dist/runtime/app/components/FlowScheduleDialog.d.vue.ts +0 -16
- package/dist/runtime/app/components/FlowScheduleDialog.vue +0 -226
- package/dist/runtime/app/components/FlowScheduleDialog.vue.d.ts +0 -16
- package/dist/runtime/app/components/FlowSchedulesList.d.vue.ts +0 -12
- package/dist/runtime/app/components/FlowSchedulesList.vue +0 -99
- package/dist/runtime/app/components/FlowSchedulesList.vue.d.ts +0 -12
- package/dist/runtime/app/components/JobScheduling.d.vue.ts +0 -6
- package/dist/runtime/app/components/JobScheduling.vue +0 -203
- package/dist/runtime/app/components/JobScheduling.vue.d.ts +0 -6
- package/dist/runtime/app/components/ListItem.d.vue.ts +0 -23
- package/dist/runtime/app/components/ListItem.vue +0 -70
- package/dist/runtime/app/components/ListItem.vue.d.ts +0 -23
- package/dist/runtime/app/components/QueueConfigDetails.d.vue.ts +0 -45
- package/dist/runtime/app/components/QueueConfigDetails.vue +0 -412
- package/dist/runtime/app/components/QueueConfigDetails.vue.d.ts +0 -45
- package/dist/runtime/app/components/StatCounter.d.vue.ts +0 -9
- package/dist/runtime/app/components/StatCounter.vue +0 -25
- package/dist/runtime/app/components/StatCounter.vue.d.ts +0 -9
- package/dist/runtime/app/components/TimelineList.d.vue.ts +0 -7
- package/dist/runtime/app/components/TimelineList.vue +0 -210
- package/dist/runtime/app/components/TimelineList.vue.d.ts +0 -7
- package/dist/runtime/app/components/nhealth/component-router.d.vue.ts +0 -46
- package/dist/runtime/app/components/nhealth/component-router.vue +0 -26
- package/dist/runtime/app/components/nhealth/component-router.vue.d.ts +0 -46
- package/dist/runtime/app/components/nhealth/component-shell.d.vue.ts +0 -24
- package/dist/runtime/app/components/nhealth/component-shell.vue +0 -89
- package/dist/runtime/app/components/nhealth/component-shell.vue.d.ts +0 -24
- package/dist/runtime/app/composables/useAnalyzedFlows.d.ts +0 -14
- package/dist/runtime/app/composables/useAnalyzedFlows.js +0 -8
- package/dist/runtime/app/composables/useComponentRouter.d.ts +0 -38
- package/dist/runtime/app/composables/useComponentRouter.js +0 -240
- package/dist/runtime/app/composables/useFlowRunTimeline.d.ts +0 -80
- package/dist/runtime/app/composables/useFlowRunTimeline.js +0 -68
- package/dist/runtime/app/composables/useFlowRuns.d.ts +0 -18
- package/dist/runtime/app/composables/useFlowRuns.js +0 -32
- package/dist/runtime/app/composables/useFlowRunsInfinite.d.ts +0 -24
- package/dist/runtime/app/composables/useFlowRunsInfinite.js +0 -123
- package/dist/runtime/app/composables/useFlowRunsPolling.d.ts +0 -9
- package/dist/runtime/app/composables/useFlowRunsPolling.js +0 -33
- package/dist/runtime/app/composables/useFlowState.d.ts +0 -125
- package/dist/runtime/app/composables/useFlowState.js +0 -211
- package/dist/runtime/app/composables/useFlowWebSocket.d.ts +0 -27
- package/dist/runtime/app/composables/useFlowWebSocket.js +0 -205
- package/dist/runtime/app/composables/useFlowsNavigation.d.ts +0 -10
- package/dist/runtime/app/composables/useFlowsNavigation.js +0 -58
- package/dist/runtime/app/composables/useQueueJobs.d.ts +0 -26
- package/dist/runtime/app/composables/useQueueJobs.js +0 -20
- package/dist/runtime/app/composables/useQueueUpdates.d.ts +0 -26
- package/dist/runtime/app/composables/useQueueUpdates.js +0 -122
- package/dist/runtime/app/composables/useQueues.d.ts +0 -45
- package/dist/runtime/app/composables/useQueues.js +0 -26
- package/dist/runtime/app/composables/useQueuesLive.d.ts +0 -19
- package/dist/runtime/app/composables/useQueuesLive.js +0 -143
- package/dist/runtime/app/pages/flows/index.d.vue.ts +0 -3
- package/dist/runtime/app/pages/flows/index.vue +0 -645
- package/dist/runtime/app/pages/flows/index.vue.d.ts +0 -3
- package/dist/runtime/app/pages/index.d.vue.ts +0 -3
- package/dist/runtime/app/pages/index.vue +0 -34
- package/dist/runtime/app/pages/index.vue.d.ts +0 -3
- package/dist/runtime/app/pages/queues/index.d.vue.ts +0 -3
- package/dist/runtime/app/pages/queues/index.vue +0 -229
- package/dist/runtime/app/pages/queues/index.vue.d.ts +0 -3
- package/dist/runtime/app/pages/queues/job.d.vue.ts +0 -3
- package/dist/runtime/app/pages/queues/job.vue +0 -262
- package/dist/runtime/app/pages/queues/job.vue.d.ts +0 -3
- package/dist/runtime/app/pages/queues/jobs.d.vue.ts +0 -3
- package/dist/runtime/app/pages/queues/jobs.vue +0 -291
- package/dist/runtime/app/pages/queues/jobs.vue.d.ts +0 -3
- package/dist/runtime/app/plugins/vueflow.client.d.ts +0 -2
- package/dist/runtime/app/plugins/vueflow.client.js +0 -11
- package/dist/runtime/constants.d.ts +0 -11
- package/dist/runtime/constants.js +0 -11
- package/dist/runtime/schema.d.ts +0 -37
- package/dist/runtime/schema.js +0 -20
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +0 -10
- package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +0 -44
- package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +0 -7
- package/dist/runtime/server/api/_flows/[name]/runs.get.js +0 -53
- package/dist/runtime/server/api/_flows/[name]/schedule.post.js +0 -57
- package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.js +0 -42
- package/dist/runtime/server/api/_flows/[name]/schedules.get.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/schedules.get.js +0 -48
- package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/start.post.js +0 -9
- package/dist/runtime/server/api/_flows/index.get.d.ts +0 -6
- package/dist/runtime/server/api/_flows/index.get.js +0 -5
- package/dist/runtime/server/api/_flows/ws.d.ts +0 -60
- package/dist/runtime/server/api/_flows/ws.js +0 -188
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +0 -9
- package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/[name]/job/index.get.js +0 -18
- package/dist/runtime/server/api/_queues/index.get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/index.get.js +0 -63
- package/dist/runtime/server/api/_queues/ws.d.ts +0 -48
- package/dist/runtime/server/api/_queues/ws.js +0 -205
- package/dist/runtime/server/plugins/00.event-store.d.ts +0 -13
- package/dist/runtime/server/plugins/00.event-store.js +0 -16
- package/dist/runtime/server/plugins/flow-management.d.ts +0 -13
- package/dist/runtime/server/plugins/flow-management.js +0 -65
- package/dist/runtime/server/plugins/queue-management.d.ts +0 -2
- package/dist/runtime/server/plugins/queue-management.js +0 -27
- package/dist/runtime/server/plugins/state-cleanup.d.ts +0 -11
- package/dist/runtime/server/plugins/state-cleanup.js +0 -93
- package/dist/runtime/server/plugins/worker-management.d.ts +0 -2
- package/dist/runtime/server/plugins/worker-management.js +0 -33
- package/dist/runtime/server/tsconfig.json +0 -3
- package/dist/runtime/server-utils/events/adapters/fileAdapter.d.ts +0 -2
- package/dist/runtime/server-utils/events/adapters/fileAdapter.js +0 -382
- package/dist/runtime/server-utils/events/adapters/memoryAdapter.d.ts +0 -2
- package/dist/runtime/server-utils/events/adapters/memoryAdapter.js +0 -171
- package/dist/runtime/server-utils/events/adapters/redis/redisAdapter.d.ts +0 -2
- package/dist/runtime/server-utils/events/adapters/redis/redisAdapter.js +0 -348
- package/dist/runtime/server-utils/events/adapters/redis/redisPubSubGateway.d.ts +0 -30
- package/dist/runtime/server-utils/events/adapters/redis/redisPubSubGateway.js +0 -82
- package/dist/runtime/server-utils/events/eventStoreFactory.d.ts +0 -19
- package/dist/runtime/server-utils/events/eventStoreFactory.js +0 -44
- package/dist/runtime/server-utils/events/streamNames.d.ts +0 -17
- package/dist/runtime/server-utils/events/streamNames.js +0 -17
- package/dist/runtime/server-utils/events/types.d.ts +0 -63
- package/dist/runtime/server-utils/events/wiring/flowWiring.js +0 -409
- package/dist/runtime/server-utils/events/wiring/registry.d.ts +0 -10
- package/dist/runtime/server-utils/events/wiring/registry.js +0 -24
- package/dist/runtime/server-utils/queue/adapters/bullmq.d.ts +0 -18
- package/dist/runtime/server-utils/queue/adapters/bullmq.js +0 -164
- package/dist/runtime/server-utils/queue/queueFactory.d.ts +0 -3
- package/dist/runtime/server-utils/queue/queueFactory.js +0 -10
- package/dist/runtime/server-utils/queue/types.d.ts +0 -47
- package/dist/runtime/server-utils/state/adapters/redis.d.ts +0 -2
- package/dist/runtime/server-utils/state/adapters/redis.js +0 -42
- package/dist/runtime/server-utils/state/stateFactory.d.ts +0 -3
- package/dist/runtime/server-utils/state/stateFactory.js +0 -17
- package/dist/runtime/server-utils/state/types.d.ts +0 -23
- package/dist/runtime/server-utils/utils/defineQueueConfig.d.ts +0 -154
- package/dist/runtime/server-utils/utils/defineQueueConfig.js +0 -2
- package/dist/runtime/server-utils/utils/defineQueueWorker.d.ts +0 -10
- package/dist/runtime/server-utils/utils/defineQueueWorker.js +0 -17
- package/dist/runtime/server-utils/utils/useEventStore.d.ts +0 -20
- package/dist/runtime/server-utils/utils/useEventStore.js +0 -119
- package/dist/runtime/server-utils/utils/useFlowEngine.d.ts +0 -9
- package/dist/runtime/server-utils/utils/useFlowEngine.js +0 -44
- package/dist/runtime/server-utils/utils/useLogs.d.ts +0 -41
- package/dist/runtime/server-utils/utils/useLogs.js +0 -74
- package/dist/runtime/server-utils/utils/useQueue.d.ts +0 -31
- package/dist/runtime/server-utils/utils/useQueue.js +0 -24
- package/dist/runtime/server-utils/worker/adapter.d.ts +0 -4
- package/dist/runtime/server-utils/worker/adapter.js +0 -66
- package/dist/runtime/server-utils/worker/runner/node.d.ts +0 -27
- package/dist/runtime/server-utils/worker/runner/node.js +0 -196
- package/dist/runtime/types.d.ts +0 -132
- /package/dist/runtime/{server-utils/events/types.js → adapters/interfaces/queue.js} +0 -0
- /package/dist/runtime/{server-utils/queue/types.js → adapters/interfaces/store.js} +0 -0
- /package/dist/runtime/{server-utils/state/types.js → adapters/interfaces/stream.js} +0 -0
- /package/dist/runtime/{server-utils/events → events}/eventBus.js +0 -0
- /package/dist/runtime/{server/plugins/00.ws-lifecycle.d.ts → nitro/plugins/01.ws-lifecycle.d.ts} +0 -0
- /package/dist/runtime/{server/plugins/00.ws-lifecycle.js → nitro/plugins/01.ws-lifecycle.js} +0 -0
- /package/dist/runtime/{server/api/_flows/[name]/schedule.post.d.ts → nitro/plugins/02.workers.d.ts} +0 -0
- /package/dist/runtime/{server-utils → nitro}/utils/useNventLogger.d.ts +0 -0
- /package/dist/runtime/{server-utils → nitro}/utils/wsPeerManager.d.ts +0 -0
- /package/dist/runtime/{server-utils → nitro}/utils/wsPeerManager.js +0 -0
- /package/dist/runtime/{python → worker/python}/get_config.py +0 -0
|
@@ -0,0 +1,1020 @@
|
|
|
1
|
+
import { getEventBus } from "../eventBus.js";
|
|
2
|
+
import { useNventLogger, useStoreAdapter, useQueueAdapter, $useAnalyzedFlows, $useFunctionRegistry, useStreamTopics, useRuntimeConfig, useScheduler } from "#imports";
|
|
3
|
+
import { createStallDetector } from "../utils/stallDetector.js";
|
|
4
|
+
export function checkPendingStepTriggers(step, emittedEvents, completedSteps) {
|
|
5
|
+
if (!step.subscribes || step.subscribes.length === 0) {
|
|
6
|
+
return true;
|
|
7
|
+
}
|
|
8
|
+
return step.subscribes.every((sub) => {
|
|
9
|
+
if (emittedEvents.has(sub)) return true;
|
|
10
|
+
const [prefix, value] = sub.split(":");
|
|
11
|
+
if (prefix === "step" && value) {
|
|
12
|
+
return completedSteps.has(value);
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export async function checkAndTriggerPendingSteps(flowName, runId, store) {
|
|
18
|
+
const logger = useNventLogger("flow-wiring");
|
|
19
|
+
try {
|
|
20
|
+
const analyzedFlows = $useAnalyzedFlows();
|
|
21
|
+
const registry = $useFunctionRegistry();
|
|
22
|
+
const queue = useQueueAdapter();
|
|
23
|
+
const { StoreSubjects } = useStreamTopics();
|
|
24
|
+
const flowDef = analyzedFlows.find((f) => f.id === flowName);
|
|
25
|
+
if (!flowDef?.steps) {
|
|
26
|
+
logger.info("No flow definition or steps found", { flowName });
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const indexKey = StoreSubjects.flowRunIndex(flowName);
|
|
30
|
+
if (!store.index.get) {
|
|
31
|
+
logger.info("No indexGet method on store", { flowName });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const flowEntry = await store.index.get(indexKey, runId);
|
|
35
|
+
if (!flowEntry?.metadata) {
|
|
36
|
+
logger.info("No flow entry or metadata found", { flowName, runId });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const streamName = StoreSubjects.flowRun(runId);
|
|
40
|
+
const allEvents = await store.stream.read(streamName);
|
|
41
|
+
const isCanceled = allEvents.some((event) => event.type === "flow.cancel");
|
|
42
|
+
if (isCanceled) {
|
|
43
|
+
logger.debug("Flow is canceled, skipping pending step triggers", { flowName, runId });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const emittedEventsObj = flowEntry.metadata.emittedEvents || {};
|
|
47
|
+
const flattenEmittedEvents = (obj, prefix = "") => {
|
|
48
|
+
const result = [];
|
|
49
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
50
|
+
if (key === "undefined" || key === "null") continue;
|
|
51
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
52
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
53
|
+
result.push(...flattenEmittedEvents(value, fullKey));
|
|
54
|
+
} else {
|
|
55
|
+
result.push(fullKey);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
const emittedEvents = new Set(
|
|
61
|
+
typeof emittedEventsObj === "object" && !Array.isArray(emittedEventsObj) ? flattenEmittedEvents(emittedEventsObj) : Array.isArray(emittedEventsObj) ? emittedEventsObj : []
|
|
62
|
+
);
|
|
63
|
+
const completedSteps = /* @__PURE__ */ new Set();
|
|
64
|
+
for (const event of allEvents) {
|
|
65
|
+
if (event.type === "step.completed" && "stepName" in event) {
|
|
66
|
+
completedSteps.add(event.stepName);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const awaitingSteps = flowEntry?.metadata?.awaitingSteps || {};
|
|
70
|
+
for (const [stepName, stepDef] of Object.entries(flowDef.steps)) {
|
|
71
|
+
const step = stepDef;
|
|
72
|
+
if (!step.subscribes || completedSteps.has(stepName)) continue;
|
|
73
|
+
const awaitState = flowEntry?.metadata?.awaitingSteps?.[stepName];
|
|
74
|
+
if (awaitState && awaitState.status === "awaiting") {
|
|
75
|
+
logger.debug("Step is awaiting, skipping trigger", {
|
|
76
|
+
flowName,
|
|
77
|
+
runId,
|
|
78
|
+
stepName,
|
|
79
|
+
awaitType: awaitState.awaitType,
|
|
80
|
+
position: awaitState.position,
|
|
81
|
+
status: awaitState.status
|
|
82
|
+
});
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (awaitState && awaitState.status === "timeout") {
|
|
86
|
+
logger.debug("Step await timed out, skipping trigger", {
|
|
87
|
+
flowName,
|
|
88
|
+
runId,
|
|
89
|
+
stepName,
|
|
90
|
+
awaitType: awaitState.awaitType
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (awaitState?.status === "resolved") {
|
|
95
|
+
logger.debug("Step await is resolved, will proceed", {
|
|
96
|
+
flowName,
|
|
97
|
+
runId,
|
|
98
|
+
stepName,
|
|
99
|
+
awaitType: awaitState.awaitType,
|
|
100
|
+
position: awaitState.position
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const isDependencyAwaiting = step.subscribes.some((sub) => {
|
|
104
|
+
const emitEvent = allEvents.find(
|
|
105
|
+
(evt) => evt.type === "emit" && evt.data?.name === sub
|
|
106
|
+
);
|
|
107
|
+
if (!emitEvent) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
const emitStepName = emitEvent.stepName;
|
|
111
|
+
if (!emitStepName) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const awaitState2 = awaitingSteps[emitStepName];
|
|
115
|
+
if (awaitState2?.position === "after" && awaitState2?.status === "awaiting") {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (awaitState2?.status === "resolved") {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
let emittingStepMeta = flowDef.steps[emitStepName];
|
|
122
|
+
if (!emittingStepMeta && emitStepName === flowDef.entry?.step) {
|
|
123
|
+
emittingStepMeta = flowDef.entry;
|
|
124
|
+
}
|
|
125
|
+
if (emittingStepMeta?.awaitAfter) {
|
|
126
|
+
const stepCompleted = allEvents.some(
|
|
127
|
+
(evt) => evt.type === "step.completed" && evt.stepName === emitStepName
|
|
128
|
+
);
|
|
129
|
+
if (stepCompleted) {
|
|
130
|
+
const awaitResolved = allEvents.some(
|
|
131
|
+
(evt) => evt.type === "await.resolved" && evt.stepName === emitStepName
|
|
132
|
+
);
|
|
133
|
+
if (!awaitResolved) {
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
});
|
|
140
|
+
if (isDependencyAwaiting) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const canTrigger = checkPendingStepTriggers(step, emittedEvents, completedSteps);
|
|
144
|
+
if (canTrigger) {
|
|
145
|
+
const flowRegistry = (registry?.flows || {})[flowName];
|
|
146
|
+
const stepMeta = flowRegistry?.steps?.[stepName];
|
|
147
|
+
if (stepMeta?.queue) {
|
|
148
|
+
const emitData = {};
|
|
149
|
+
const subscribes = step.subscribes || [];
|
|
150
|
+
for (const sub of subscribes) {
|
|
151
|
+
const emitEvent = allEvents.find(
|
|
152
|
+
(evt) => evt.type === "emit" && evt.data?.name === sub
|
|
153
|
+
);
|
|
154
|
+
if (emitEvent && emitEvent.data?.payload !== void 0) {
|
|
155
|
+
emitData[sub] = emitEvent.data.payload;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const payload = {
|
|
159
|
+
flowId: runId,
|
|
160
|
+
flowName,
|
|
161
|
+
input: emitData
|
|
162
|
+
// Keyed by event name
|
|
163
|
+
};
|
|
164
|
+
if (awaitState?.status === "resolved" && awaitState?.position === "before") {
|
|
165
|
+
payload.awaitResolved = true;
|
|
166
|
+
payload.awaitData = awaitState.triggerData;
|
|
167
|
+
}
|
|
168
|
+
const jobId = `${runId}__${stepName}`;
|
|
169
|
+
const worker = registry?.workers?.find(
|
|
170
|
+
(w) => w?.flow?.step === stepName && w?.queue?.name === stepMeta.queue
|
|
171
|
+
);
|
|
172
|
+
const defaultOpts = worker?.queue?.defaultJobOptions || {};
|
|
173
|
+
const opts = { ...defaultOpts, jobId };
|
|
174
|
+
try {
|
|
175
|
+
await queue.enqueue(stepMeta.queue, { name: stepName, data: payload, opts });
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
} catch (err) {
|
|
182
|
+
logger.warn("Failed to check pending steps", {
|
|
183
|
+
flowName,
|
|
184
|
+
runId,
|
|
185
|
+
error: err?.message
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export function analyzeFlowCompletion(flowSteps, entryStep, events, entryStepDef) {
|
|
190
|
+
const isCanceled = events.some((event) => event.type === "flow.cancel");
|
|
191
|
+
const allSteps = entryStep ? [entryStep, ...Object.keys(flowSteps)] : Object.keys(flowSteps);
|
|
192
|
+
const completedSteps = /* @__PURE__ */ new Set();
|
|
193
|
+
const failedSteps = /* @__PURE__ */ new Set();
|
|
194
|
+
const retriedSteps = /* @__PURE__ */ new Map();
|
|
195
|
+
let startedAt = 0;
|
|
196
|
+
let completedAt = 0;
|
|
197
|
+
for (const event of events) {
|
|
198
|
+
if (event.type === "flow.start") {
|
|
199
|
+
startedAt = typeof event.ts === "string" ? new Date(event.ts).getTime() : 0;
|
|
200
|
+
}
|
|
201
|
+
if (event.type === "flow.cancel") {
|
|
202
|
+
completedAt = typeof event.ts === "string" ? new Date(event.ts).getTime() : Date.now();
|
|
203
|
+
}
|
|
204
|
+
if (event.type === "step.completed" && "stepName" in event) {
|
|
205
|
+
completedSteps.add(event.stepName);
|
|
206
|
+
}
|
|
207
|
+
if (event.type === "step.retry" && "stepName" in event) {
|
|
208
|
+
const currentAttempts = retriedSteps.get(event.stepName) || 0;
|
|
209
|
+
retriedSteps.set(event.stepName, currentAttempts + 1);
|
|
210
|
+
}
|
|
211
|
+
if (event.type === "step.failed" && "stepName" in event) {
|
|
212
|
+
failedSteps.add(event.stepName);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const finalFailedSteps = /* @__PURE__ */ new Set();
|
|
216
|
+
for (const stepName of Array.from(failedSteps)) {
|
|
217
|
+
let lastRetryIndex = -1;
|
|
218
|
+
let lastFailedIndex = -1;
|
|
219
|
+
for (let i = 0; i < events.length; i++) {
|
|
220
|
+
const event = events[i];
|
|
221
|
+
if ("stepName" in event && event.stepName === stepName) {
|
|
222
|
+
if (event.type === "step.retry") {
|
|
223
|
+
lastRetryIndex = i;
|
|
224
|
+
}
|
|
225
|
+
if (event.type === "step.failed") {
|
|
226
|
+
lastFailedIndex = i;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (lastFailedIndex > lastRetryIndex) {
|
|
231
|
+
finalFailedSteps.add(stepName);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const totalSteps = allSteps.length;
|
|
235
|
+
const hasFinalFailures = finalFailedSteps.size > 0;
|
|
236
|
+
const stepDependencies = /* @__PURE__ */ new Map();
|
|
237
|
+
const stepDependents = /* @__PURE__ */ new Map();
|
|
238
|
+
for (const stepName of allSteps) {
|
|
239
|
+
stepDependencies.set(stepName, /* @__PURE__ */ new Set());
|
|
240
|
+
stepDependents.set(stepName, /* @__PURE__ */ new Set());
|
|
241
|
+
}
|
|
242
|
+
for (const [stepName, stepDef] of Object.entries(flowSteps)) {
|
|
243
|
+
const step = stepDef;
|
|
244
|
+
const subscribes = step.subscribes || [];
|
|
245
|
+
for (const sub of subscribes) {
|
|
246
|
+
for (const [emitStepName, emitStepDef] of Object.entries(flowSteps)) {
|
|
247
|
+
const emitStep = emitStepDef;
|
|
248
|
+
const emits = emitStep.emits || [];
|
|
249
|
+
if (emits.some((emit) => sub === `${emitStepName}.${emit}` || sub === emit)) {
|
|
250
|
+
stepDependencies.get(stepName)?.add(emitStepName);
|
|
251
|
+
stepDependents.get(emitStepName)?.add(stepName);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (entryStep && entryStepDef?.emits) {
|
|
255
|
+
const entryEmits = entryStepDef.emits || [];
|
|
256
|
+
if (entryEmits.some((emit) => sub === `${entryStep}.${emit}` || sub === emit)) {
|
|
257
|
+
stepDependencies.get(stepName)?.add(entryStep);
|
|
258
|
+
stepDependents.get(entryStep)?.add(stepName);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
let hasBlockingFailure = false;
|
|
264
|
+
let hasCriticalLayerFailure = false;
|
|
265
|
+
if (hasFinalFailures) {
|
|
266
|
+
for (const failedStepName of Array.from(finalFailedSteps)) {
|
|
267
|
+
const dependents = stepDependents.get(failedStepName);
|
|
268
|
+
if (dependents && dependents.size > 0) {
|
|
269
|
+
for (const dependentName of Array.from(dependents)) {
|
|
270
|
+
if (!completedSteps.has(dependentName)) {
|
|
271
|
+
hasBlockingFailure = true;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (hasBlockingFailure) break;
|
|
277
|
+
}
|
|
278
|
+
const layerGroups = /* @__PURE__ */ new Map();
|
|
279
|
+
for (const stepName of allSteps) {
|
|
280
|
+
const deps = stepDependencies.get(stepName);
|
|
281
|
+
const depsKey = Array.from(deps || []).sort().join(",");
|
|
282
|
+
if (!layerGroups.has(depsKey)) {
|
|
283
|
+
layerGroups.set(depsKey, /* @__PURE__ */ new Set());
|
|
284
|
+
}
|
|
285
|
+
layerGroups.get(depsKey)?.add(stepName);
|
|
286
|
+
}
|
|
287
|
+
for (const [_depsKey, layerSteps] of Array.from(layerGroups)) {
|
|
288
|
+
const layerHasFailures = Array.from(layerSteps).some((s) => finalFailedSteps.has(s));
|
|
289
|
+
if (!layerHasFailures) continue;
|
|
290
|
+
const allLayerStepsFailed = Array.from(layerSteps).every(
|
|
291
|
+
(s) => finalFailedSteps.has(s)
|
|
292
|
+
);
|
|
293
|
+
if (allLayerStepsFailed) {
|
|
294
|
+
const hasLeafNode = Array.from(layerSteps).some((s) => {
|
|
295
|
+
const deps = stepDependents.get(s);
|
|
296
|
+
return !deps || deps.size === 0;
|
|
297
|
+
});
|
|
298
|
+
if (hasLeafNode) {
|
|
299
|
+
hasCriticalLayerFailure = true;
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (isCanceled) {
|
|
306
|
+
return {
|
|
307
|
+
status: "canceled",
|
|
308
|
+
totalSteps,
|
|
309
|
+
completedSteps: completedSteps.size,
|
|
310
|
+
startedAt,
|
|
311
|
+
completedAt
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (hasBlockingFailure || hasCriticalLayerFailure) {
|
|
315
|
+
return {
|
|
316
|
+
status: "failed",
|
|
317
|
+
totalSteps,
|
|
318
|
+
completedSteps: completedSteps.size,
|
|
319
|
+
startedAt,
|
|
320
|
+
completedAt: Date.now()
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
const allStepsTerminal = allSteps.every(
|
|
324
|
+
(step) => completedSteps.has(step) || finalFailedSteps.has(step)
|
|
325
|
+
);
|
|
326
|
+
let status = "running";
|
|
327
|
+
if (allStepsTerminal) {
|
|
328
|
+
status = "completed";
|
|
329
|
+
completedAt = Date.now();
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
status,
|
|
333
|
+
totalSteps,
|
|
334
|
+
completedSteps: completedSteps.size,
|
|
335
|
+
startedAt,
|
|
336
|
+
completedAt
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
export function createFlowWiring() {
|
|
340
|
+
const bus = getEventBus();
|
|
341
|
+
const unsubs = [];
|
|
342
|
+
let wired = false;
|
|
343
|
+
let stallDetector;
|
|
344
|
+
const publishingTerminalEvents = /* @__PURE__ */ new Set();
|
|
345
|
+
const indexFlowRun = async (flowName, flowId, timestamp, metadata) => {
|
|
346
|
+
const logger = useNventLogger("flow-wiring");
|
|
347
|
+
try {
|
|
348
|
+
const store = useStoreAdapter();
|
|
349
|
+
const { StoreSubjects } = useStreamTopics();
|
|
350
|
+
const indexKey = StoreSubjects.flowRunIndex(flowName);
|
|
351
|
+
if (!store.index.add) {
|
|
352
|
+
throw new Error("StoreAdapter does not support indexAdd");
|
|
353
|
+
}
|
|
354
|
+
await store.index.add(indexKey, flowId, timestamp, metadata);
|
|
355
|
+
} catch (err) {
|
|
356
|
+
logger.error("Failed to index run", { error: err });
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
const flowProcessingChain = /* @__PURE__ */ new Map();
|
|
360
|
+
const cleanupTimers = /* @__PURE__ */ new Map();
|
|
361
|
+
async function start() {
|
|
362
|
+
if (wired) return;
|
|
363
|
+
wired = true;
|
|
364
|
+
const logger = useNventLogger("flow-wiring");
|
|
365
|
+
const { StoreSubjects } = useStreamTopics();
|
|
366
|
+
logger.info("Flow wiring starting");
|
|
367
|
+
const store = useStoreAdapter();
|
|
368
|
+
if (!store || !store.stream.append) {
|
|
369
|
+
logger.error("StoreAdapter not properly initialized or missing append method", {
|
|
370
|
+
hasStore: !!store,
|
|
371
|
+
hasAppend: !!(store && store.stream.append)
|
|
372
|
+
});
|
|
373
|
+
throw new Error("StoreAdapter not initialized");
|
|
374
|
+
}
|
|
375
|
+
const handlePersistence = async (e) => {
|
|
376
|
+
try {
|
|
377
|
+
if (e.id && e.ts) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const runId = e.runId;
|
|
381
|
+
if (!runId) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const flowName = e.flowName;
|
|
385
|
+
if (!flowName) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const streamName = StoreSubjects.flowRun(runId);
|
|
389
|
+
if (!e.type) {
|
|
390
|
+
logger.error("Event missing type field", { event: e });
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const eventData = {
|
|
394
|
+
type: e.type,
|
|
395
|
+
runId: e.runId,
|
|
396
|
+
flowName: e.flowName,
|
|
397
|
+
data: e.data
|
|
398
|
+
};
|
|
399
|
+
if ("stepName" in e && e.stepName) eventData.stepName = e.stepName;
|
|
400
|
+
if ("stepId" in e && e.stepId) eventData.stepId = e.stepId;
|
|
401
|
+
if ("attempt" in e && e.attempt) eventData.attempt = e.attempt;
|
|
402
|
+
const persistedEvent = await store.stream.append(streamName, eventData);
|
|
403
|
+
await bus.publish(persistedEvent);
|
|
404
|
+
if (e.type === "flow.completed" || e.type === "flow.failed") {
|
|
405
|
+
const publishKey = `${runId}:terminal`;
|
|
406
|
+
setTimeout(() => {
|
|
407
|
+
try {
|
|
408
|
+
publishingTerminalEvents.delete(publishKey);
|
|
409
|
+
} catch (err) {
|
|
410
|
+
logger.debug("Error cleaning up terminal event tracker", { publishKey, error: err?.message });
|
|
411
|
+
}
|
|
412
|
+
}, 200);
|
|
413
|
+
logger.info("Stored terminal event", {
|
|
414
|
+
type: e.type,
|
|
415
|
+
flowName,
|
|
416
|
+
runId,
|
|
417
|
+
id: persistedEvent.id
|
|
418
|
+
});
|
|
419
|
+
} else {
|
|
420
|
+
logger.debug("Stored event", {
|
|
421
|
+
type: e.type,
|
|
422
|
+
flowName,
|
|
423
|
+
runId,
|
|
424
|
+
stepName: "stepName" in e ? e.stepName : void 0,
|
|
425
|
+
id: persistedEvent.id
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
} catch (err) {
|
|
429
|
+
logger.error("ERROR persisting event", {
|
|
430
|
+
type: e.type,
|
|
431
|
+
runId: e.runId,
|
|
432
|
+
flowName: e.flowName,
|
|
433
|
+
error: err?.message
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
const handleFlowStats = async (e) => {
|
|
438
|
+
try {
|
|
439
|
+
if (!e.id || !e.ts) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
const flowName = e.flowName;
|
|
443
|
+
if (!flowName) return;
|
|
444
|
+
const flowIndexKey = StoreSubjects.flowIndex();
|
|
445
|
+
if (e.type === "flow.start") {
|
|
446
|
+
if (store.index.increment) {
|
|
447
|
+
await store.index.increment(flowIndexKey, flowName, "stats.total", 1);
|
|
448
|
+
await store.index.increment(flowIndexKey, flowName, "stats.running", 1);
|
|
449
|
+
}
|
|
450
|
+
if (store.index.updateWithRetry) {
|
|
451
|
+
await store.index.updateWithRetry(flowIndexKey, flowName, {
|
|
452
|
+
lastRunAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
logger.debug("Updated flow stats for start", { flowName });
|
|
456
|
+
} else if (e.type === "flow.completed") {
|
|
457
|
+
if (store.index.increment) {
|
|
458
|
+
await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
|
|
459
|
+
await store.index.increment(flowIndexKey, flowName, "stats.success", 1);
|
|
460
|
+
}
|
|
461
|
+
if (store.index.updateWithRetry) {
|
|
462
|
+
await store.index.updateWithRetry(flowIndexKey, flowName, {
|
|
463
|
+
lastCompletedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
logger.debug("Updated flow stats for completion", { flowName });
|
|
467
|
+
} else if (e.type === "flow.failed") {
|
|
468
|
+
if (store.index.increment) {
|
|
469
|
+
await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
|
|
470
|
+
await store.index.increment(flowIndexKey, flowName, "stats.failure", 1);
|
|
471
|
+
}
|
|
472
|
+
if (store.index.updateWithRetry) {
|
|
473
|
+
await store.index.updateWithRetry(flowIndexKey, flowName, {
|
|
474
|
+
lastCompletedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
logger.debug("Updated flow stats for failure", { flowName });
|
|
478
|
+
} else if (e.type === "flow.cancel") {
|
|
479
|
+
if (store.index.increment) {
|
|
480
|
+
await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
|
|
481
|
+
await store.index.increment(flowIndexKey, flowName, "stats.cancel", 1);
|
|
482
|
+
}
|
|
483
|
+
if (store.index.updateWithRetry) {
|
|
484
|
+
await store.index.updateWithRetry(flowIndexKey, flowName, {
|
|
485
|
+
lastCompletedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
logger.debug("Updated flow stats for cancellation", { flowName });
|
|
489
|
+
} else if (e.type === "flow.stalled") {
|
|
490
|
+
if (store.index.increment && e.data?.previousStatus) {
|
|
491
|
+
if (e.data.previousStatus === "awaiting") {
|
|
492
|
+
await store.index.increment(flowIndexKey, flowName, "stats.awaiting", -1);
|
|
493
|
+
logger.debug("Updated flow stats for stalled detection (was awaiting)", { flowName });
|
|
494
|
+
} else if (e.data.previousStatus === "running") {
|
|
495
|
+
await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
|
|
496
|
+
logger.debug("Updated flow stats for stalled detection (was running)", { flowName });
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
} else if (e.type === "await.registered") {
|
|
500
|
+
if (store.index.increment) {
|
|
501
|
+
await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
|
|
502
|
+
await store.index.increment(flowIndexKey, flowName, "stats.awaiting", 1);
|
|
503
|
+
}
|
|
504
|
+
logger.debug("Updated flow stats for await registered", { flowName });
|
|
505
|
+
} else if (e.type === "await.resolved" || e.type === "await.timeout") {
|
|
506
|
+
if (store.index.increment) {
|
|
507
|
+
await store.index.increment(flowIndexKey, flowName, "stats.awaiting", -1);
|
|
508
|
+
await store.index.increment(flowIndexKey, flowName, "stats.running", 1);
|
|
509
|
+
}
|
|
510
|
+
logger.debug("Updated flow stats for await resolved/timeout", { flowName, type: e.type });
|
|
511
|
+
}
|
|
512
|
+
try {
|
|
513
|
+
const indexEntry = await store.index.get(flowIndexKey, flowName);
|
|
514
|
+
if (indexEntry) {
|
|
515
|
+
await bus.publish({
|
|
516
|
+
type: "flow.stats.updated",
|
|
517
|
+
flowName,
|
|
518
|
+
id: indexEntry.id,
|
|
519
|
+
metadata: indexEntry.metadata,
|
|
520
|
+
ts: Date.now()
|
|
521
|
+
});
|
|
522
|
+
logger.debug("Published flow stats update event to bus", { flowName });
|
|
523
|
+
}
|
|
524
|
+
} catch (err) {
|
|
525
|
+
logger.warn("Failed to publish flow stats update event", {
|
|
526
|
+
flowName,
|
|
527
|
+
error: err?.message
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
} catch (err) {
|
|
531
|
+
logger.warn("Failed to update flow stats", {
|
|
532
|
+
type: e.type,
|
|
533
|
+
flowName: e.flowName,
|
|
534
|
+
error: err?.message
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
const handleOrchestration = async (e) => {
|
|
539
|
+
try {
|
|
540
|
+
if (e.id && e.ts) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (e.type === "flow.completed" || e.type === "flow.failed") {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const runId = e.runId;
|
|
547
|
+
if (!runId) return;
|
|
548
|
+
const flowName = e.flowName;
|
|
549
|
+
if (!flowName) return;
|
|
550
|
+
const streamName = StoreSubjects.flowRun(runId);
|
|
551
|
+
const indexKey = StoreSubjects.flowRunIndex(flowName);
|
|
552
|
+
if (e.type === "flow.start") {
|
|
553
|
+
const timestamp = Date.now();
|
|
554
|
+
await indexFlowRun(flowName, runId, timestamp, {
|
|
555
|
+
status: "running",
|
|
556
|
+
startedAt: timestamp,
|
|
557
|
+
lastActivityAt: timestamp,
|
|
558
|
+
// Initialize for stall detection
|
|
559
|
+
stepCount: 0,
|
|
560
|
+
completedSteps: 0,
|
|
561
|
+
emittedEvents: {}
|
|
562
|
+
// Object for atomic updates
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
if (e.type === "flow.cancel") {
|
|
566
|
+
try {
|
|
567
|
+
if (store.index.updateWithRetry) {
|
|
568
|
+
await store.index.updateWithRetry(indexKey, runId, {
|
|
569
|
+
status: "canceled",
|
|
570
|
+
completedAt: Date.now()
|
|
571
|
+
});
|
|
572
|
+
logger.info("Marked flow as canceled", { flowName, runId });
|
|
573
|
+
}
|
|
574
|
+
} catch (err) {
|
|
575
|
+
logger.warn("Failed to update canceled status", {
|
|
576
|
+
flowName,
|
|
577
|
+
runId,
|
|
578
|
+
error: err?.message
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (e.type === "step.started" || e.type === "step.completed" || e.type === "step.failed" || e.type === "step.retry") {
|
|
583
|
+
if (stallDetector) {
|
|
584
|
+
await stallDetector.updateActivity(flowName, runId);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (e.type === "step.completed") {
|
|
588
|
+
try {
|
|
589
|
+
if (store.index.increment) {
|
|
590
|
+
const newCount = await store.index.increment(indexKey, runId, "completedSteps", 1);
|
|
591
|
+
logger.debug("Incremented completedSteps", {
|
|
592
|
+
flowName,
|
|
593
|
+
runId,
|
|
594
|
+
stepName: "stepName" in e ? e.stepName : "unknown",
|
|
595
|
+
newCount
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
} catch (err) {
|
|
599
|
+
logger.warn("Failed to update completedSteps", {
|
|
600
|
+
flowName,
|
|
601
|
+
runId,
|
|
602
|
+
error: err?.message
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (e.type === "await.registered") {
|
|
607
|
+
const awaitEvent = e;
|
|
608
|
+
const { stepName, awaitType, position, config: config2 } = awaitEvent;
|
|
609
|
+
try {
|
|
610
|
+
if (store.index.updateWithRetry) {
|
|
611
|
+
const now = Date.now();
|
|
612
|
+
let timeoutAt;
|
|
613
|
+
if (awaitType === "time" && config2.delay) {
|
|
614
|
+
timeoutAt = now + config2.delay;
|
|
615
|
+
} else if (awaitType === "schedule" && config2.nextOccurrence) {
|
|
616
|
+
timeoutAt = config2.nextOccurrence;
|
|
617
|
+
} else if (config2.timeout) {
|
|
618
|
+
timeoutAt = now + config2.timeout;
|
|
619
|
+
}
|
|
620
|
+
if (!timeoutAt) {
|
|
621
|
+
timeoutAt = now + 24 * 60 * 60 * 1e3;
|
|
622
|
+
}
|
|
623
|
+
const updatePayload = {
|
|
624
|
+
awaitingSteps: {
|
|
625
|
+
[stepName]: {
|
|
626
|
+
status: "awaiting",
|
|
627
|
+
awaitType,
|
|
628
|
+
position,
|
|
629
|
+
config: config2,
|
|
630
|
+
registeredAt: now,
|
|
631
|
+
timeoutAt
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
await store.index.updateWithRetry(indexKey, runId, updatePayload);
|
|
636
|
+
logger.info("Await registered in index", {
|
|
637
|
+
runId,
|
|
638
|
+
stepName,
|
|
639
|
+
awaitType,
|
|
640
|
+
position,
|
|
641
|
+
timeoutAt: new Date(timeoutAt).toISOString()
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
} catch (err) {
|
|
645
|
+
logger.error("Error updating await status", {
|
|
646
|
+
runId,
|
|
647
|
+
stepName,
|
|
648
|
+
error: err?.message
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (e.type === "await.resolved") {
|
|
653
|
+
const awaitEvent = e;
|
|
654
|
+
const { stepName, triggerData } = awaitEvent;
|
|
655
|
+
try {
|
|
656
|
+
if (store.index.updateWithRetry) {
|
|
657
|
+
await store.index.updateWithRetry(indexKey, runId, {
|
|
658
|
+
awaitingSteps: {
|
|
659
|
+
[stepName]: {
|
|
660
|
+
status: "resolved",
|
|
661
|
+
triggerData
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
await checkAndTriggerPendingSteps(flowName, runId, store);
|
|
667
|
+
} catch (err) {
|
|
668
|
+
logger.error("Error handling await resolution", {
|
|
669
|
+
runId,
|
|
670
|
+
stepName,
|
|
671
|
+
error: err?.message
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
if (e.type === "await.timeout") {
|
|
676
|
+
const timeoutEvent = e;
|
|
677
|
+
const { stepName, timeoutAction, position, awaitType } = timeoutEvent;
|
|
678
|
+
const action = timeoutAction || "fail";
|
|
679
|
+
logger.warn("Await timeout occurred", {
|
|
680
|
+
runId,
|
|
681
|
+
stepName,
|
|
682
|
+
awaitType,
|
|
683
|
+
position,
|
|
684
|
+
action
|
|
685
|
+
});
|
|
686
|
+
try {
|
|
687
|
+
if (action === "fail") {
|
|
688
|
+
if (store.index.updateWithRetry) {
|
|
689
|
+
await store.index.updateWithRetry(indexKey, runId, {
|
|
690
|
+
awaitingSteps: {
|
|
691
|
+
[stepName]: {
|
|
692
|
+
status: "timeout",
|
|
693
|
+
timedOutAt: Date.now()
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
bus.publish({
|
|
699
|
+
type: "step.failed",
|
|
700
|
+
runId,
|
|
701
|
+
flowName,
|
|
702
|
+
stepName,
|
|
703
|
+
stepId: `${runId}__${stepName}__timeout`,
|
|
704
|
+
attempt: 1,
|
|
705
|
+
data: {
|
|
706
|
+
error: `Await timeout: ${awaitType} await exceeded timeout`
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
} else if (action === "continue") {
|
|
710
|
+
if (store.index.updateWithRetry) {
|
|
711
|
+
await store.index.updateWithRetry(indexKey, runId, {
|
|
712
|
+
awaitingSteps: {
|
|
713
|
+
[stepName]: {
|
|
714
|
+
status: "resolved",
|
|
715
|
+
triggerData: null,
|
|
716
|
+
timedOutAt: Date.now()
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
logger.info("Await timeout - continuing with null data", { runId, stepName });
|
|
722
|
+
await checkAndTriggerPendingSteps(flowName, runId, store);
|
|
723
|
+
}
|
|
724
|
+
} catch (err) {
|
|
725
|
+
logger.error("Error handling await timeout", {
|
|
726
|
+
runId,
|
|
727
|
+
stepName,
|
|
728
|
+
error: err?.message
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (e.type === "emit") {
|
|
733
|
+
const eventName = e.data?.name || e.data?.topic;
|
|
734
|
+
if (!eventName) {
|
|
735
|
+
logger.warn("Emit event missing name/topic", { flowName, runId, data: e.data });
|
|
736
|
+
} else {
|
|
737
|
+
try {
|
|
738
|
+
if (!store.index.updateWithRetry) {
|
|
739
|
+
logger.warn("StoreAdapter does not support indexUpdateWithRetry");
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
const timestamp = Date.now();
|
|
743
|
+
const eventParts = eventName.split(".");
|
|
744
|
+
const emittedEventsUpdate = {};
|
|
745
|
+
let current = emittedEventsUpdate;
|
|
746
|
+
for (let i = 0; i < eventParts.length - 1; i++) {
|
|
747
|
+
current[eventParts[i]] = {};
|
|
748
|
+
current = current[eventParts[i]];
|
|
749
|
+
}
|
|
750
|
+
current[eventParts[eventParts.length - 1]] = timestamp;
|
|
751
|
+
const updatePayload = {
|
|
752
|
+
emittedEvents: emittedEventsUpdate
|
|
753
|
+
};
|
|
754
|
+
await store.index.updateWithRetry(indexKey, runId, updatePayload);
|
|
755
|
+
logger.debug("Tracked emit event", {
|
|
756
|
+
flowName,
|
|
757
|
+
runId,
|
|
758
|
+
name: eventName,
|
|
759
|
+
timestamp
|
|
760
|
+
});
|
|
761
|
+
} catch (err) {
|
|
762
|
+
logger.warn("Failed to track emitted event", {
|
|
763
|
+
flowName,
|
|
764
|
+
runId,
|
|
765
|
+
event: eventName,
|
|
766
|
+
error: err?.message
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (e.type === "step.completed") {
|
|
772
|
+
try {
|
|
773
|
+
await checkAndTriggerPendingSteps(flowName, runId, store);
|
|
774
|
+
} catch (err) {
|
|
775
|
+
logger.error("Error checking pending steps", {
|
|
776
|
+
flowName,
|
|
777
|
+
runId,
|
|
778
|
+
error: err.message
|
|
779
|
+
});
|
|
780
|
+
throw err;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (e.type === "step.completed" || e.type === "step.failed") {
|
|
784
|
+
try {
|
|
785
|
+
const allEvents = await store.stream.read(streamName);
|
|
786
|
+
const analyzedFlows = $useAnalyzedFlows();
|
|
787
|
+
const flowDef = analyzedFlows.find((f) => f.id === flowName);
|
|
788
|
+
if (flowDef?.steps) {
|
|
789
|
+
const entryStepName = flowDef.entry?.step;
|
|
790
|
+
const entryStepDef = flowDef.entry;
|
|
791
|
+
const analysis = analyzeFlowCompletion(flowDef.steps, entryStepName, allEvents, entryStepDef);
|
|
792
|
+
const updateMetadata = {
|
|
793
|
+
status: analysis.status,
|
|
794
|
+
stepCount: analysis.totalSteps
|
|
795
|
+
};
|
|
796
|
+
if (analysis.status !== "running" && analysis.completedAt) {
|
|
797
|
+
updateMetadata.completedAt = analysis.completedAt;
|
|
798
|
+
}
|
|
799
|
+
if (store.index.get) {
|
|
800
|
+
const currentEntry = await store.index.get(indexKey, runId);
|
|
801
|
+
const awaitingStepsObj = currentEntry?.metadata?.awaitingSteps || {};
|
|
802
|
+
let hasActiveAwaits = false;
|
|
803
|
+
let hasTimedOutAwaits = false;
|
|
804
|
+
for (const [_stepName, awaitState] of Object.entries(awaitingStepsObj)) {
|
|
805
|
+
if (awaitState?.status === "awaiting") {
|
|
806
|
+
hasActiveAwaits = true;
|
|
807
|
+
} else if (awaitState?.status === "timeout") {
|
|
808
|
+
hasTimedOutAwaits = true;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (hasActiveAwaits) {
|
|
812
|
+
updateMetadata.status = "awaiting";
|
|
813
|
+
}
|
|
814
|
+
if (hasTimedOutAwaits) {
|
|
815
|
+
updateMetadata.status = "failed";
|
|
816
|
+
if (!updateMetadata.completedAt) {
|
|
817
|
+
updateMetadata.completedAt = Date.now();
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (store.index.updateWithRetry) {
|
|
822
|
+
await store.index.updateWithRetry(indexKey, runId, updateMetadata);
|
|
823
|
+
}
|
|
824
|
+
const finalStatus = updateMetadata.status || analysis.status;
|
|
825
|
+
if (finalStatus === "completed" || finalStatus === "failed") {
|
|
826
|
+
const eventType = finalStatus === "completed" ? "flow.completed" : "flow.failed";
|
|
827
|
+
let currentStatus = null;
|
|
828
|
+
if (store.index.get) {
|
|
829
|
+
const currentEntry = await store.index.get(indexKey, runId);
|
|
830
|
+
currentStatus = currentEntry?.metadata?.status;
|
|
831
|
+
}
|
|
832
|
+
const terminalEventExists = allEvents.some((evt) => evt.type === "flow.completed" || evt.type === "flow.failed");
|
|
833
|
+
const publishKey = `${runId}:terminal`;
|
|
834
|
+
const alreadyPublishing = publishingTerminalEvents.has(publishKey);
|
|
835
|
+
if (terminalEventExists) {
|
|
836
|
+
logger.debug("Flow terminal event already exists in stream, skipping publish", {
|
|
837
|
+
flowName,
|
|
838
|
+
runId,
|
|
839
|
+
currentStatus,
|
|
840
|
+
eventType
|
|
841
|
+
});
|
|
842
|
+
} else if (alreadyPublishing) {
|
|
843
|
+
logger.debug("Flow terminal event already being published, skipping duplicate", {
|
|
844
|
+
flowName,
|
|
845
|
+
runId,
|
|
846
|
+
eventType
|
|
847
|
+
});
|
|
848
|
+
} else {
|
|
849
|
+
publishingTerminalEvents.add(publishKey);
|
|
850
|
+
logger.info("Publishing terminal event to bus", {
|
|
851
|
+
flowName,
|
|
852
|
+
runId,
|
|
853
|
+
eventType
|
|
854
|
+
});
|
|
855
|
+
await bus.publish({
|
|
856
|
+
type: eventType,
|
|
857
|
+
runId,
|
|
858
|
+
flowName,
|
|
859
|
+
data: {}
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
} catch (err) {
|
|
865
|
+
logger.warn("Failed to analyze flow completion", {
|
|
866
|
+
flowName,
|
|
867
|
+
runId,
|
|
868
|
+
error: err?.message
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
} catch (err) {
|
|
873
|
+
logger.error("ERROR handling event", {
|
|
874
|
+
type: e.type,
|
|
875
|
+
runId: e.runId,
|
|
876
|
+
flowName: e.flowName,
|
|
877
|
+
error: err?.message
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
const processEventSequentially = async (event) => {
|
|
882
|
+
const runId = event.runId;
|
|
883
|
+
if (!runId) {
|
|
884
|
+
await handlePersistence(event);
|
|
885
|
+
await handleOrchestration(event);
|
|
886
|
+
await handleFlowStats(event);
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
const previousProcessing = flowProcessingChain.get(runId) || Promise.resolve();
|
|
890
|
+
const currentProcessing = previousProcessing.then(async () => {
|
|
891
|
+
try {
|
|
892
|
+
await handlePersistence(event);
|
|
893
|
+
await handleOrchestration(event);
|
|
894
|
+
await handleFlowStats(event);
|
|
895
|
+
} catch (err) {
|
|
896
|
+
logger.error("Error in sequential event processing", {
|
|
897
|
+
runId,
|
|
898
|
+
type: event.type,
|
|
899
|
+
error: err.message,
|
|
900
|
+
stack: err.stack
|
|
901
|
+
});
|
|
902
|
+
} finally {
|
|
903
|
+
const existingTimer = cleanupTimers.get(runId);
|
|
904
|
+
if (existingTimer) {
|
|
905
|
+
clearTimeout(existingTimer);
|
|
906
|
+
}
|
|
907
|
+
const timer = setTimeout(() => {
|
|
908
|
+
if (flowProcessingChain.get(runId) === currentProcessing) {
|
|
909
|
+
flowProcessingChain.delete(runId);
|
|
910
|
+
cleanupTimers.delete(runId);
|
|
911
|
+
}
|
|
912
|
+
}, 6e4);
|
|
913
|
+
cleanupTimers.set(runId, timer);
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
flowProcessingChain.set(runId, currentProcessing);
|
|
917
|
+
return currentProcessing;
|
|
918
|
+
};
|
|
919
|
+
const eventTypes = [
|
|
920
|
+
"flow.start",
|
|
921
|
+
"flow.completed",
|
|
922
|
+
"flow.failed",
|
|
923
|
+
"flow.cancel",
|
|
924
|
+
"step.started",
|
|
925
|
+
"step.completed",
|
|
926
|
+
"step.failed",
|
|
927
|
+
"step.retry",
|
|
928
|
+
"await.registered",
|
|
929
|
+
"await.resolved",
|
|
930
|
+
"await.timeout",
|
|
931
|
+
"log",
|
|
932
|
+
"emit",
|
|
933
|
+
"state"
|
|
934
|
+
];
|
|
935
|
+
for (const type of eventTypes) {
|
|
936
|
+
unsubs.push(bus.onType(type, processEventSequentially));
|
|
937
|
+
}
|
|
938
|
+
const config = useRuntimeConfig();
|
|
939
|
+
const flowConfig = config.nvent.flow || {};
|
|
940
|
+
stallDetector = createStallDetector(store, flowConfig.stallDetection);
|
|
941
|
+
if (flowConfig.stallDetection?.enabled) {
|
|
942
|
+
await stallDetector.start();
|
|
943
|
+
const scheduleConfig = stallDetector.getScheduleConfig();
|
|
944
|
+
if (scheduleConfig.enabled) {
|
|
945
|
+
try {
|
|
946
|
+
const scheduler = useScheduler();
|
|
947
|
+
logger.info("Scheduling periodic stall detector from flowWiring", {
|
|
948
|
+
checkInterval: `${scheduleConfig.interval / 1e3}s`
|
|
949
|
+
});
|
|
950
|
+
const jobId = await scheduler.schedule({
|
|
951
|
+
id: "stall-detection",
|
|
952
|
+
name: "Flow Stall Detection",
|
|
953
|
+
type: "interval",
|
|
954
|
+
interval: scheduleConfig.interval,
|
|
955
|
+
handler: async () => {
|
|
956
|
+
if (!stallDetector || !wired) {
|
|
957
|
+
logger.debug("Stall detector handler called but wiring stopped");
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
960
|
+
try {
|
|
961
|
+
logger.info("Stall detector running periodic check");
|
|
962
|
+
const analyzedFlows = $useAnalyzedFlows();
|
|
963
|
+
const flowNames = analyzedFlows.map((f) => f.id).filter(Boolean);
|
|
964
|
+
if (flowNames.length > 0) {
|
|
965
|
+
await stallDetector.checkFlowsForStalls(flowNames);
|
|
966
|
+
}
|
|
967
|
+
} catch (error) {
|
|
968
|
+
logger.error("Stall detector periodic check failed", {
|
|
969
|
+
error: error.message,
|
|
970
|
+
stack: error.stack
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
},
|
|
974
|
+
metadata: {
|
|
975
|
+
component: "stall-detector",
|
|
976
|
+
stallTimeout: scheduleConfig.stallTimeout,
|
|
977
|
+
checkInterval: scheduleConfig.interval
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
stallDetector.setSchedulerJobId(jobId);
|
|
981
|
+
logger.info("Stall detector started and scheduled", { jobId });
|
|
982
|
+
} catch (error) {
|
|
983
|
+
logger.error("Failed to schedule stall detector - periodic checks disabled", {
|
|
984
|
+
error: error.message,
|
|
985
|
+
stack: error.stack
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
} else {
|
|
989
|
+
logger.info("Stall detector started (periodic check disabled)");
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
async function stop() {
|
|
994
|
+
const logger = useNventLogger("flow-wiring");
|
|
995
|
+
if (stallDetector) {
|
|
996
|
+
try {
|
|
997
|
+
await stallDetector.stop();
|
|
998
|
+
stallDetector = void 0;
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
logger.error("Error stopping stall detector", {
|
|
1001
|
+
error: error.message
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
for (const u of unsubs.splice(0)) {
|
|
1006
|
+
try {
|
|
1007
|
+
u();
|
|
1008
|
+
} catch {
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
publishingTerminalEvents.clear();
|
|
1012
|
+
for (const timer of cleanupTimers.values()) {
|
|
1013
|
+
clearTimeout(timer);
|
|
1014
|
+
}
|
|
1015
|
+
cleanupTimers.clear();
|
|
1016
|
+
flowProcessingChain.clear();
|
|
1017
|
+
wired = false;
|
|
1018
|
+
}
|
|
1019
|
+
return { start, stop };
|
|
1020
|
+
}
|