nvent 0.5.15 → 1.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/dist/module.d.mts +181 -8
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +1257 -1196
  4. package/dist/runtime/app/composables/useFunctionCall.d.ts +17 -0
  5. package/dist/runtime/app/composables/useFunctionCall.js +25 -0
  6. package/dist/runtime/app/composables/useNventStream.d.ts +23 -0
  7. package/dist/runtime/app/composables/useNventStream.js +58 -0
  8. package/dist/runtime/nitro/plugins/00.iii-lifecycle.d.ts +19 -0
  9. package/dist/runtime/nitro/plugins/00.iii-lifecycle.js +62 -0
  10. package/dist/runtime/nitro/plugins/01.iii-worker.d.ts +15 -0
  11. package/dist/runtime/nitro/plugins/01.iii-worker.js +91 -0
  12. package/dist/runtime/nitro/routes/stream-proxy.d.ts +17 -0
  13. package/dist/runtime/nitro/routes/stream-proxy.js +45 -0
  14. package/dist/runtime/nitro/utils/console.d.ts +32 -0
  15. package/dist/runtime/nitro/utils/console.js +95 -0
  16. package/dist/runtime/nitro/utils/defineFunction.d.ts +378 -3
  17. package/dist/runtime/nitro/utils/defineFunction.js +224 -4
  18. package/dist/runtime/nitro/utils/engine.d.ts +25 -0
  19. package/dist/runtime/nitro/utils/engine.js +130 -0
  20. package/dist/runtime/nitro/utils/nventDir.d.ts +1 -0
  21. package/dist/runtime/nitro/utils/nventDir.js +20 -0
  22. package/dist/runtime/nitro/utils/useIii.d.ts +23 -0
  23. package/dist/runtime/nitro/utils/useIii.js +14 -0
  24. package/dist/runtime/nitro/utils/workers/node.d.ts +24 -0
  25. package/dist/runtime/nitro/utils/workers/node.js +57 -0
  26. package/dist/runtime/nitro/utils/workers/python.d.ts +67 -0
  27. package/dist/runtime/nitro/utils/workers/python.js +220 -0
  28. package/dist/runtime/python/nvent.py +208 -0
  29. package/dist/runtime/python/worker_runtime.py +659 -0
  30. package/dist/runtime/virtual.d.ts +17 -0
  31. package/dist/types.d.mts +5 -1
  32. package/package.json +11 -14
  33. package/dist/runtime/adapters/base/index.d.ts +0 -6
  34. package/dist/runtime/adapters/base/index.js +0 -1
  35. package/dist/runtime/adapters/base/store-validator.d.ts +0 -48
  36. package/dist/runtime/adapters/base/store-validator.js +0 -147
  37. package/dist/runtime/adapters/builtin/file-queue.d.ts +0 -67
  38. package/dist/runtime/adapters/builtin/file-queue.js +0 -499
  39. package/dist/runtime/adapters/builtin/file-store.d.ts +0 -32
  40. package/dist/runtime/adapters/builtin/file-store.js +0 -206
  41. package/dist/runtime/adapters/builtin/file-stream.d.ts +0 -39
  42. package/dist/runtime/adapters/builtin/file-stream.js +0 -56
  43. package/dist/runtime/adapters/builtin/index.d.ts +0 -10
  44. package/dist/runtime/adapters/builtin/index.js +0 -5
  45. package/dist/runtime/adapters/builtin/memory-queue.d.ts +0 -52
  46. package/dist/runtime/adapters/builtin/memory-queue.js +0 -243
  47. package/dist/runtime/adapters/builtin/memory-store.d.ts +0 -69
  48. package/dist/runtime/adapters/builtin/memory-store.js +0 -357
  49. package/dist/runtime/adapters/builtin/memory-stream.d.ts +0 -21
  50. package/dist/runtime/adapters/builtin/memory-stream.js +0 -56
  51. package/dist/runtime/adapters/factory.d.ts +0 -31
  52. package/dist/runtime/adapters/factory.js +0 -135
  53. package/dist/runtime/adapters/index.d.ts +0 -8
  54. package/dist/runtime/adapters/index.js +0 -3
  55. package/dist/runtime/adapters/interfaces/index.d.ts +0 -11
  56. package/dist/runtime/adapters/interfaces/index.js +0 -3
  57. package/dist/runtime/adapters/interfaces/queue.d.ts +0 -155
  58. package/dist/runtime/adapters/interfaces/queue.js +0 -0
  59. package/dist/runtime/adapters/interfaces/store.d.ts +0 -299
  60. package/dist/runtime/adapters/interfaces/store.js +0 -0
  61. package/dist/runtime/adapters/interfaces/stream.d.ts +0 -62
  62. package/dist/runtime/adapters/interfaces/stream.js +0 -0
  63. package/dist/runtime/adapters/registry.d.ts +0 -85
  64. package/dist/runtime/adapters/registry.js +0 -161
  65. package/dist/runtime/config/index.d.ts +0 -23
  66. package/dist/runtime/config/index.js +0 -200
  67. package/dist/runtime/config/types.d.ts +0 -439
  68. package/dist/runtime/config/types.js +0 -0
  69. package/dist/runtime/events/eventBus.d.ts +0 -20
  70. package/dist/runtime/events/eventBus.js +0 -35
  71. package/dist/runtime/events/types.d.ts +0 -144
  72. package/dist/runtime/events/types.js +0 -0
  73. package/dist/runtime/events/utils/scheduleTrigger.d.ts +0 -8
  74. package/dist/runtime/events/utils/scheduleTrigger.js +0 -71
  75. package/dist/runtime/events/utils/stallDetector.d.ts +0 -83
  76. package/dist/runtime/events/utils/stallDetector.js +0 -318
  77. package/dist/runtime/events/utils/triggerRuntime.d.ts +0 -58
  78. package/dist/runtime/events/utils/triggerRuntime.js +0 -212
  79. package/dist/runtime/events/wiring/flowWiring.d.ts +0 -34
  80. package/dist/runtime/events/wiring/flowWiring.js +0 -1258
  81. package/dist/runtime/events/wiring/registry.d.ts +0 -19
  82. package/dist/runtime/events/wiring/registry.js +0 -43
  83. package/dist/runtime/events/wiring/stateWiring.d.ts +0 -37
  84. package/dist/runtime/events/wiring/stateWiring.js +0 -92
  85. package/dist/runtime/events/wiring/streamWiring.d.ts +0 -36
  86. package/dist/runtime/events/wiring/streamWiring.js +0 -156
  87. package/dist/runtime/events/wiring/triggerWiring.d.ts +0 -21
  88. package/dist/runtime/events/wiring/triggerWiring.js +0 -422
  89. package/dist/runtime/nitro/plugins/00.adapters.d.ts +0 -14
  90. package/dist/runtime/nitro/plugins/00.adapters.js +0 -73
  91. package/dist/runtime/nitro/plugins/01.ws-lifecycle.d.ts +0 -5
  92. package/dist/runtime/nitro/plugins/01.ws-lifecycle.js +0 -69
  93. package/dist/runtime/nitro/plugins/02.workers.d.ts +0 -2
  94. package/dist/runtime/nitro/plugins/02.workers.js +0 -92
  95. package/dist/runtime/nitro/plugins/03.triggers.d.ts +0 -12
  96. package/dist/runtime/nitro/plugins/03.triggers.js +0 -55
  97. package/dist/runtime/nitro/routes/webhook.await.d.ts +0 -23
  98. package/dist/runtime/nitro/routes/webhook.await.js +0 -112
  99. package/dist/runtime/nitro/routes/webhook.trigger.d.ts +0 -86
  100. package/dist/runtime/nitro/routes/webhook.trigger.js +0 -73
  101. package/dist/runtime/nitro/utils/adapters.d.ts +0 -66
  102. package/dist/runtime/nitro/utils/adapters.js +0 -51
  103. package/dist/runtime/nitro/utils/awaitPatterns/event.d.ts +0 -15
  104. package/dist/runtime/nitro/utils/awaitPatterns/event.js +0 -128
  105. package/dist/runtime/nitro/utils/awaitPatterns/index.d.ts +0 -28
  106. package/dist/runtime/nitro/utils/awaitPatterns/index.js +0 -55
  107. package/dist/runtime/nitro/utils/awaitPatterns/schedule.d.ts +0 -16
  108. package/dist/runtime/nitro/utils/awaitPatterns/schedule.js +0 -83
  109. package/dist/runtime/nitro/utils/awaitPatterns/time.d.ts +0 -15
  110. package/dist/runtime/nitro/utils/awaitPatterns/time.js +0 -71
  111. package/dist/runtime/nitro/utils/awaitPatterns/webhook.d.ts +0 -15
  112. package/dist/runtime/nitro/utils/awaitPatterns/webhook.js +0 -128
  113. package/dist/runtime/nitro/utils/defineFunctionConfig.d.ts +0 -378
  114. package/dist/runtime/nitro/utils/defineFunctionConfig.js +0 -3
  115. package/dist/runtime/nitro/utils/defineHooks.d.ts +0 -95
  116. package/dist/runtime/nitro/utils/defineHooks.js +0 -9
  117. package/dist/runtime/nitro/utils/registerAdapter.d.ts +0 -59
  118. package/dist/runtime/nitro/utils/registerAdapter.js +0 -13
  119. package/dist/runtime/nitro/utils/useAwait.d.ts +0 -83
  120. package/dist/runtime/nitro/utils/useAwait.js +0 -169
  121. package/dist/runtime/nitro/utils/useEventManager.d.ts +0 -15
  122. package/dist/runtime/nitro/utils/useEventManager.js +0 -26
  123. package/dist/runtime/nitro/utils/useFlow.d.ts +0 -66
  124. package/dist/runtime/nitro/utils/useFlow.js +0 -310
  125. package/dist/runtime/nitro/utils/useHookRegistry.d.ts +0 -40
  126. package/dist/runtime/nitro/utils/useHookRegistry.js +0 -25
  127. package/dist/runtime/nitro/utils/useNventLogger.d.ts +0 -42
  128. package/dist/runtime/nitro/utils/useNventLogger.js +0 -54
  129. package/dist/runtime/nitro/utils/useRunContext.d.ts +0 -6
  130. package/dist/runtime/nitro/utils/useRunContext.js +0 -102
  131. package/dist/runtime/nitro/utils/useStreamTopics.d.ts +0 -83
  132. package/dist/runtime/nitro/utils/useStreamTopics.js +0 -94
  133. package/dist/runtime/nitro/utils/useTrigger.d.ts +0 -150
  134. package/dist/runtime/nitro/utils/useTrigger.js +0 -311
  135. package/dist/runtime/nitro/utils/wsPeerManager.d.ts +0 -44
  136. package/dist/runtime/nitro/utils/wsPeerManager.js +0 -32
  137. package/dist/runtime/scheduler/index.d.ts +0 -33
  138. package/dist/runtime/scheduler/index.js +0 -42
  139. package/dist/runtime/scheduler/scheduler.d.ts +0 -132
  140. package/dist/runtime/scheduler/scheduler.js +0 -799
  141. package/dist/runtime/scheduler/types.d.ts +0 -122
  142. package/dist/runtime/scheduler/types.js +0 -0
  143. package/dist/runtime/worker/node/runner.d.ts +0 -95
  144. package/dist/runtime/worker/node/runner.js +0 -272
  145. package/dist/runtime/worker/python/get_config.py +0 -64
  146. package/dist/runtime/worker/system/awaitHandlers.d.ts +0 -27
  147. package/dist/runtime/worker/system/awaitHandlers.js +0 -230
  148. package/dist/runtime/worker/system/index.d.ts +0 -24
  149. package/dist/runtime/worker/system/index.js +0 -39
@@ -1,1258 +0,0 @@
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
- import { SYSTEM_HANDLERS } from "../../worker/system/index.js";
5
- export function checkPendingStepTriggers(step, emittedEvents, completedSteps) {
6
- if (!step.subscribes || step.subscribes.length === 0) {
7
- return true;
8
- }
9
- return step.subscribes.every((sub) => {
10
- if (emittedEvents.has(sub)) return true;
11
- const [prefix, value] = sub.split(":");
12
- if (prefix === "step" && value) {
13
- return completedSteps.has(value);
14
- }
15
- return false;
16
- });
17
- }
18
- export async function checkAndTriggerPendingSteps(flowName, runId, store) {
19
- const logger = useNventLogger("flow-wiring");
20
- try {
21
- const analyzedFlows = $useAnalyzedFlows();
22
- const registry = $useFunctionRegistry();
23
- const queue = useQueueAdapter();
24
- const { StoreSubjects } = useStreamTopics();
25
- const flowDef = analyzedFlows.find((f) => f.id === flowName);
26
- if (!flowDef?.steps) {
27
- logger.info("No flow definition or steps found", { flowName });
28
- return;
29
- }
30
- const indexKey = StoreSubjects.flowRunIndex(flowName);
31
- if (!store.index.get) {
32
- logger.info("No indexGet method on store", { flowName });
33
- return;
34
- }
35
- const flowEntry = await store.index.get(indexKey, runId);
36
- if (!flowEntry?.metadata) {
37
- logger.info("No flow entry or metadata found", { flowName, runId });
38
- return;
39
- }
40
- const streamName = StoreSubjects.flowRun(runId);
41
- const allEvents = await store.stream.read(streamName);
42
- const isCanceled = allEvents.some((event) => event.type === "flow.cancel");
43
- if (isCanceled) {
44
- logger.debug("Flow is canceled, skipping pending step triggers", { flowName, runId });
45
- return;
46
- }
47
- const emittedEventsObj = flowEntry.metadata.emittedEvents || {};
48
- const flattenEmittedEvents = (obj, prefix = "") => {
49
- const result = [];
50
- for (const [key, value] of Object.entries(obj)) {
51
- if (key === "undefined" || key === "null") continue;
52
- const fullKey = prefix ? `${prefix}.${key}` : key;
53
- if (value && typeof value === "object" && !Array.isArray(value)) {
54
- result.push(...flattenEmittedEvents(value, fullKey));
55
- } else {
56
- result.push(fullKey);
57
- }
58
- }
59
- return result;
60
- };
61
- const emittedEvents = new Set(
62
- typeof emittedEventsObj === "object" && !Array.isArray(emittedEventsObj) ? flattenEmittedEvents(emittedEventsObj) : Array.isArray(emittedEventsObj) ? emittedEventsObj : []
63
- );
64
- const completedSteps = /* @__PURE__ */ new Set();
65
- for (const event of allEvents) {
66
- if (event.type === "step.completed" && "stepName" in event) {
67
- completedSteps.add(event.stepName);
68
- }
69
- }
70
- const awaitingSteps = flowEntry?.metadata?.awaitingSteps || {};
71
- for (const [stepName, stepDef] of Object.entries(flowDef.steps)) {
72
- const step = stepDef;
73
- if (!step.subscribes || completedSteps.has(stepName)) continue;
74
- const awaitBeforeKey = `${stepName}:before`;
75
- const awaitState = flowEntry?.metadata?.awaitingSteps?.[awaitBeforeKey];
76
- if (awaitState && awaitState.status === "awaiting") continue;
77
- if (awaitState && awaitState.status === "timeout") continue;
78
- const isDependencyAwaiting = step.subscribes.some((sub) => {
79
- const emitEvent = allEvents.find(
80
- (evt) => evt.type === "emit" && evt.data?.name === sub
81
- );
82
- if (!emitEvent) return false;
83
- const emitStepName = emitEvent.stepName;
84
- if (!emitStepName) {
85
- return false;
86
- }
87
- const awaitAfterKey = `${emitStepName}:after`;
88
- const awaitState2 = awaitingSteps[awaitAfterKey];
89
- if (awaitState2?.status === "awaiting") {
90
- return true;
91
- }
92
- if (awaitState2?.status === "resolved") {
93
- return false;
94
- }
95
- let emittingStepMeta = flowDef.steps[emitStepName];
96
- if (!emittingStepMeta && emitStepName === flowDef.entry?.step) {
97
- emittingStepMeta = flowDef.entry;
98
- }
99
- if (emittingStepMeta?.awaitAfter) {
100
- const stepCompleted = allEvents.some(
101
- (evt) => evt.type === "step.completed" && evt.stepName === emitStepName
102
- );
103
- if (stepCompleted) {
104
- const awaitResolved = allEvents.some(
105
- (evt) => evt.type === "await.resolved" && evt.stepName === emitStepName && evt.position === "after"
106
- );
107
- if (!awaitResolved) return true;
108
- }
109
- }
110
- return false;
111
- });
112
- if (isDependencyAwaiting) continue;
113
- const canTrigger = checkPendingStepTriggers(step, emittedEvents, completedSteps);
114
- if (canTrigger && step.awaitBefore) {
115
- if (awaitState?.status === "awaiting") continue;
116
- if (awaitState?.status !== "resolved") {
117
- try {
118
- const emitData = {};
119
- const subscribes = step.subscribes || [];
120
- for (const sub of subscribes) {
121
- const emitEvent = allEvents.find(
122
- (evt) => evt.type === "emit" && evt.data?.name === sub
123
- );
124
- if (emitEvent && emitEvent.data?.payload !== void 0) {
125
- emitData[sub] = emitEvent.data.payload;
126
- }
127
- }
128
- const payload = {
129
- flowId: runId,
130
- flowName,
131
- stepName,
132
- position: "before",
133
- awaitConfig: step.awaitBefore,
134
- input: emitData
135
- };
136
- const jobId = `${runId}__${stepName}__await-register-before`;
137
- const flowRegistry = (registry?.flows || {})[flowName];
138
- const stepMeta = flowRegistry?.steps?.[stepName];
139
- let stepQueue = stepMeta?.queue;
140
- if (!stepQueue && flowRegistry?.entry?.step === stepName) {
141
- stepQueue = flowRegistry.entry.queue;
142
- }
143
- if (!stepQueue && registry?.workers) {
144
- const worker = registry.workers.find((w) => {
145
- const flowNames = w?.flow?.names || (w?.flow?.name ? [w?.flow?.name] : []);
146
- const stepMatch = w?.flow?.step === stepName || Array.isArray(w?.flow?.step) && w?.flow?.step.includes(stepName);
147
- return flowNames.includes(flowName) && stepMatch;
148
- });
149
- stepQueue = worker?.queue?.name;
150
- }
151
- if (!stepQueue) {
152
- logger.error("Cannot find queue for step", {
153
- stepName,
154
- flowName,
155
- availableSteps: Object.keys(flowRegistry?.steps || {}),
156
- entryStep: flowRegistry?.entry?.step
157
- });
158
- throw new Error(`Cannot register await: queue not found for step ${stepName} in flow ${flowName}`);
159
- }
160
- const analyzedAwaitStep = (flowDef.analyzed?.steps || {})[stepName];
161
- const awaitStepTimeout = analyzedAwaitStep?.stepTimeout;
162
- await queue.enqueue(stepQueue, {
163
- name: SYSTEM_HANDLERS.AWAIT_REGISTER,
164
- data: payload,
165
- opts: { jobId, timeout: awaitStepTimeout }
166
- });
167
- } catch (err) {
168
- logger.error("Failed to register awaitBefore pattern", {
169
- flowName,
170
- stepName,
171
- error: err.message
172
- });
173
- }
174
- continue;
175
- }
176
- }
177
- const shouldEnqueueStep = canTrigger && (!step.awaitBefore || awaitState?.status === "resolved");
178
- if (shouldEnqueueStep) {
179
- const flowRegistry = (registry?.flows || {})[flowName];
180
- const stepMeta = flowRegistry?.steps?.[stepName];
181
- if (stepMeta?.queue) {
182
- const emitData = {};
183
- const subscribes = step.subscribes || [];
184
- for (const sub of subscribes) {
185
- const emitEvent = allEvents.find(
186
- (evt) => evt.type === "emit" && evt.data?.name === sub
187
- );
188
- if (emitEvent && emitEvent.data?.payload !== void 0) {
189
- emitData[sub] = emitEvent.data.payload;
190
- }
191
- }
192
- const payload = {
193
- flowId: runId,
194
- flowName,
195
- input: emitData
196
- // Keyed by event name
197
- };
198
- const isAwaitResuming = awaitState?.status === "resolved" && awaitState?.position === "before";
199
- if (isAwaitResuming) {
200
- payload.awaitResolved = true;
201
- payload.awaitData = awaitState.triggerData;
202
- payload.awaitPosition = "before";
203
- }
204
- const jobId = isAwaitResuming ? `${runId}__${stepName}__resumed` : `${runId}__${stepName}`;
205
- const worker = registry?.workers?.find(
206
- (w) => w?.flow?.step === stepName && w?.queue?.name === stepMeta.queue
207
- );
208
- const defaultOpts = worker?.queue?.defaultJobOptions || {};
209
- const analyzedStep = (flowDef.analyzed?.steps || {})[stepName];
210
- const stepTimeout = analyzedStep?.stepTimeout;
211
- const opts = { ...defaultOpts, jobId, timeout: stepTimeout };
212
- try {
213
- await queue.enqueue(stepMeta.queue, { name: stepName, data: payload, opts });
214
- } catch {
215
- }
216
- }
217
- }
218
- }
219
- } catch (err) {
220
- logger.warn("Failed to check pending steps", {
221
- flowName,
222
- runId,
223
- error: err?.message
224
- });
225
- }
226
- }
227
- export function analyzeFlowCompletion(flowSteps, entryStep, events, entryStepDef) {
228
- const isCanceled = events.some((event) => event.type === "flow.cancel");
229
- const allSteps = entryStep ? [entryStep, ...Object.keys(flowSteps)] : Object.keys(flowSteps);
230
- const completedSteps = /* @__PURE__ */ new Set();
231
- const failedSteps = /* @__PURE__ */ new Set();
232
- const retriedSteps = /* @__PURE__ */ new Map();
233
- let startedAt = 0;
234
- let completedAt = 0;
235
- for (const event of events) {
236
- if (event.type === "flow.start") {
237
- startedAt = typeof event.ts === "string" ? new Date(event.ts).getTime() : 0;
238
- }
239
- if (event.type === "flow.cancel") {
240
- completedAt = typeof event.ts === "string" ? new Date(event.ts).getTime() : Date.now();
241
- }
242
- if (event.type === "step.completed" && "stepName" in event) {
243
- completedSteps.add(event.stepName);
244
- }
245
- if (event.type === "step.retry" && "stepName" in event) {
246
- const currentAttempts = retriedSteps.get(event.stepName) || 0;
247
- retriedSteps.set(event.stepName, currentAttempts + 1);
248
- }
249
- if (event.type === "step.failed" && "stepName" in event) {
250
- failedSteps.add(event.stepName);
251
- }
252
- }
253
- const finalFailedSteps = /* @__PURE__ */ new Set();
254
- for (const stepName of Array.from(failedSteps)) {
255
- let lastRetryIndex = -1;
256
- let lastFailedIndex = -1;
257
- for (let i = 0; i < events.length; i++) {
258
- const event = events[i];
259
- if ("stepName" in event && event.stepName === stepName) {
260
- if (event.type === "step.retry") {
261
- lastRetryIndex = i;
262
- }
263
- if (event.type === "step.failed") {
264
- lastFailedIndex = i;
265
- }
266
- }
267
- }
268
- if (lastFailedIndex > lastRetryIndex) {
269
- finalFailedSteps.add(stepName);
270
- }
271
- }
272
- const totalSteps = allSteps.length;
273
- const hasFinalFailures = finalFailedSteps.size > 0;
274
- const stepDependencies = /* @__PURE__ */ new Map();
275
- const stepDependents = /* @__PURE__ */ new Map();
276
- for (const stepName of allSteps) {
277
- stepDependencies.set(stepName, /* @__PURE__ */ new Set());
278
- stepDependents.set(stepName, /* @__PURE__ */ new Set());
279
- }
280
- for (const [stepName, stepDef] of Object.entries(flowSteps)) {
281
- const step = stepDef;
282
- const subscribes = step.subscribes || [];
283
- for (const sub of subscribes) {
284
- for (const [emitStepName, emitStepDef] of Object.entries(flowSteps)) {
285
- const emitStep = emitStepDef;
286
- const emits = emitStep.emits || [];
287
- if (emits.some((emit) => sub === `${emitStepName}.${emit}` || sub === emit)) {
288
- stepDependencies.get(stepName)?.add(emitStepName);
289
- stepDependents.get(emitStepName)?.add(stepName);
290
- }
291
- }
292
- if (entryStep && entryStepDef?.emits) {
293
- const entryEmits = entryStepDef.emits || [];
294
- if (entryEmits.some((emit) => sub === `${entryStep}.${emit}` || sub === emit)) {
295
- stepDependencies.get(stepName)?.add(entryStep);
296
- stepDependents.get(entryStep)?.add(stepName);
297
- }
298
- }
299
- }
300
- }
301
- let hasBlockingFailure = false;
302
- let hasCriticalLayerFailure = false;
303
- if (hasFinalFailures) {
304
- for (const failedStepName of Array.from(finalFailedSteps)) {
305
- const dependents = stepDependents.get(failedStepName);
306
- if (dependents && dependents.size > 0) {
307
- for (const dependentName of Array.from(dependents)) {
308
- if (!completedSteps.has(dependentName)) {
309
- hasBlockingFailure = true;
310
- break;
311
- }
312
- }
313
- }
314
- if (hasBlockingFailure) break;
315
- }
316
- const layerGroups = /* @__PURE__ */ new Map();
317
- for (const stepName of allSteps) {
318
- const deps = stepDependencies.get(stepName);
319
- const depsKey = Array.from(deps || []).sort().join(",");
320
- if (!layerGroups.has(depsKey)) {
321
- layerGroups.set(depsKey, /* @__PURE__ */ new Set());
322
- }
323
- layerGroups.get(depsKey)?.add(stepName);
324
- }
325
- for (const [_depsKey, layerSteps] of Array.from(layerGroups)) {
326
- const layerHasFailures = Array.from(layerSteps).some((s) => finalFailedSteps.has(s));
327
- if (!layerHasFailures) continue;
328
- const allLayerStepsFailed = Array.from(layerSteps).every(
329
- (s) => finalFailedSteps.has(s)
330
- );
331
- if (allLayerStepsFailed) {
332
- const hasLeafNode = Array.from(layerSteps).some((s) => {
333
- const deps = stepDependents.get(s);
334
- return !deps || deps.size === 0;
335
- });
336
- if (hasLeafNode) {
337
- hasCriticalLayerFailure = true;
338
- break;
339
- }
340
- }
341
- }
342
- }
343
- if (isCanceled) {
344
- return {
345
- status: "canceled",
346
- totalSteps,
347
- completedSteps: completedSteps.size,
348
- startedAt,
349
- completedAt
350
- };
351
- }
352
- if (hasBlockingFailure || hasCriticalLayerFailure) {
353
- return {
354
- status: "failed",
355
- totalSteps,
356
- completedSteps: completedSteps.size,
357
- startedAt,
358
- completedAt: Date.now()
359
- };
360
- }
361
- const allStepsTerminal = allSteps.every(
362
- (step) => completedSteps.has(step) || finalFailedSteps.has(step)
363
- );
364
- let status = "running";
365
- if (allStepsTerminal) {
366
- status = "completed";
367
- completedAt = Date.now();
368
- }
369
- return {
370
- status,
371
- totalSteps,
372
- completedSteps: completedSteps.size,
373
- startedAt,
374
- completedAt
375
- };
376
- }
377
- export function createFlowWiring() {
378
- const bus = getEventBus();
379
- const unsubs = [];
380
- let wired = false;
381
- let stallDetector;
382
- const publishingTerminalEvents = /* @__PURE__ */ new Set();
383
- const indexFlowRun = async (flowName, flowId, timestamp, metadata) => {
384
- const logger = useNventLogger("flow-wiring");
385
- try {
386
- const store = useStoreAdapter();
387
- const { StoreSubjects } = useStreamTopics();
388
- const indexKey = StoreSubjects.flowRunIndex(flowName);
389
- if (!store.index.add) {
390
- throw new Error("StoreAdapter does not support indexAdd");
391
- }
392
- await store.index.add(indexKey, flowId, timestamp, metadata);
393
- } catch (err) {
394
- logger.error("Failed to index run", { error: err });
395
- }
396
- };
397
- const flowProcessingChain = /* @__PURE__ */ new Map();
398
- const cleanupTimers = /* @__PURE__ */ new Map();
399
- async function start() {
400
- if (wired) return;
401
- wired = true;
402
- const logger = useNventLogger("flow-wiring");
403
- const { StoreSubjects } = useStreamTopics();
404
- logger.info("Flow wiring starting");
405
- const store = useStoreAdapter();
406
- if (!store || !store.stream.append) {
407
- logger.error("StoreAdapter not properly initialized or missing append method", {
408
- hasStore: !!store,
409
- hasAppend: !!(store && store.stream.append)
410
- });
411
- throw new Error("StoreAdapter not initialized");
412
- }
413
- const handlePersistence = async (e) => {
414
- try {
415
- if (e.id && e.ts) {
416
- return;
417
- }
418
- const runId = e.runId;
419
- if (!runId) {
420
- return;
421
- }
422
- const flowName = e.flowName;
423
- if (!flowName) {
424
- return;
425
- }
426
- const streamName = StoreSubjects.flowRun(runId);
427
- if (!e.type) {
428
- logger.error("Event missing type field", { event: e });
429
- return;
430
- }
431
- const eventData = {
432
- type: e.type,
433
- runId: e.runId,
434
- flowName: e.flowName,
435
- data: e.data
436
- };
437
- if ("stepName" in e && e.stepName) eventData.stepName = e.stepName;
438
- if ("stepId" in e && e.stepId) eventData.stepId = e.stepId;
439
- if ("attempt" in e && e.attempt) eventData.attempt = e.attempt;
440
- const persistedEvent = await store.stream.append(streamName, eventData);
441
- await bus.publish(persistedEvent);
442
- if (e.type === "flow.completed" || e.type === "flow.failed") {
443
- try {
444
- const scheduler = useScheduler();
445
- const flowJobs = await scheduler.getJobsByPattern(runId);
446
- for (const job of flowJobs) {
447
- await scheduler.unschedule(job.id);
448
- logger.debug(`Unscheduled job for ${e.type} flow: ${job.id}`);
449
- }
450
- logger.debug(`Unscheduled ${flowJobs.length} scheduled jobs for ${e.type} flow runId '${runId}'`, {
451
- jobs: flowJobs.map((j) => j.id)
452
- });
453
- } catch (error) {
454
- logger.debug(`Could not unschedule jobs for runId '${runId}'`, {
455
- error: error.message
456
- });
457
- }
458
- const publishKey = `${runId}:terminal`;
459
- setTimeout(() => {
460
- try {
461
- publishingTerminalEvents.delete(publishKey);
462
- } catch (err) {
463
- logger.debug("Error cleaning up terminal event tracker", { publishKey, error: err?.message });
464
- }
465
- }, 200);
466
- logger.info("Stored terminal event", {
467
- type: e.type,
468
- flowName,
469
- runId,
470
- id: persistedEvent.id
471
- });
472
- } else {
473
- logger.debug("Stored event", {
474
- type: e.type,
475
- flowName,
476
- runId,
477
- stepName: "stepName" in e ? e.stepName : void 0,
478
- id: persistedEvent.id
479
- });
480
- }
481
- } catch (err) {
482
- logger.error("ERROR persisting event", {
483
- type: e.type,
484
- runId: e.runId,
485
- flowName: e.flowName,
486
- error: err?.message
487
- });
488
- }
489
- };
490
- const handleFlowStats = async (e) => {
491
- try {
492
- if (!e.id || !e.ts) {
493
- return;
494
- }
495
- const flowName = e.flowName;
496
- if (!flowName) return;
497
- const flowIndexKey = StoreSubjects.flowIndex();
498
- if (e.type === "flow.start") {
499
- if (store.index.increment) {
500
- await store.index.increment(flowIndexKey, flowName, "stats.total", 1);
501
- await store.index.increment(flowIndexKey, flowName, "stats.running", 1);
502
- }
503
- if (store.index.updateWithRetry) {
504
- await store.index.updateWithRetry(flowIndexKey, flowName, {
505
- lastRunAt: (/* @__PURE__ */ new Date()).toISOString()
506
- });
507
- }
508
- logger.debug("Updated flow stats for start", { flowName });
509
- } else if (e.type === "flow.completed") {
510
- if (store.index.increment) {
511
- await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
512
- await store.index.increment(flowIndexKey, flowName, "stats.success", 1);
513
- }
514
- if (store.index.updateWithRetry) {
515
- await store.index.updateWithRetry(flowIndexKey, flowName, {
516
- lastCompletedAt: (/* @__PURE__ */ new Date()).toISOString()
517
- });
518
- }
519
- logger.debug("Updated flow stats for completion", { flowName });
520
- } else if (e.type === "flow.failed") {
521
- if (store.index.increment) {
522
- await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
523
- await store.index.increment(flowIndexKey, flowName, "stats.failure", 1);
524
- }
525
- if (store.index.updateWithRetry) {
526
- await store.index.updateWithRetry(flowIndexKey, flowName, {
527
- lastCompletedAt: (/* @__PURE__ */ new Date()).toISOString()
528
- });
529
- }
530
- logger.debug("Updated flow stats for failure", { flowName });
531
- } else if (e.type === "flow.cancel") {
532
- if (store.index.increment) {
533
- const previousStatus = e.data?.previousStatus;
534
- if (previousStatus === "awaiting") {
535
- await store.index.increment(flowIndexKey, flowName, "stats.awaiting", -1);
536
- } else {
537
- await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
538
- }
539
- await store.index.increment(flowIndexKey, flowName, "stats.cancel", 1);
540
- }
541
- if (store.index.updateWithRetry) {
542
- await store.index.updateWithRetry(flowIndexKey, flowName, {
543
- lastCompletedAt: (/* @__PURE__ */ new Date()).toISOString()
544
- });
545
- }
546
- logger.debug("Updated flow stats for cancellation", { flowName, previousStatus: e.data?.previousStatus });
547
- } else if (e.type === "flow.stalled") {
548
- if (store.index.increment && e.data?.previousStatus) {
549
- if (e.data.previousStatus === "awaiting") {
550
- await store.index.increment(flowIndexKey, flowName, "stats.awaiting", -1);
551
- logger.debug("Updated flow stats for stalled detection (was awaiting)", { flowName });
552
- } else if (e.data.previousStatus === "running") {
553
- await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
554
- logger.debug("Updated flow stats for stalled detection (was running)", { flowName });
555
- }
556
- }
557
- } else if (e.type === "await.registered") {
558
- if (store.index.increment) {
559
- await store.index.increment(flowIndexKey, flowName, "stats.running", -1);
560
- await store.index.increment(flowIndexKey, flowName, "stats.awaiting", 1);
561
- }
562
- logger.debug("Updated flow stats for await registered", { flowName });
563
- } else if (e.type === "await.resolved" || e.type === "await.timeout") {
564
- if (store.index.increment) {
565
- await store.index.increment(flowIndexKey, flowName, "stats.awaiting", -1);
566
- await store.index.increment(flowIndexKey, flowName, "stats.running", 1);
567
- }
568
- logger.debug("Updated flow stats for await resolved/timeout", { flowName, type: e.type });
569
- }
570
- try {
571
- const indexEntry = await store.index.get(flowIndexKey, flowName);
572
- if (indexEntry) {
573
- await bus.publish({
574
- type: "flow.stats.updated",
575
- flowName,
576
- id: indexEntry.id,
577
- metadata: indexEntry.metadata,
578
- ts: Date.now()
579
- });
580
- logger.debug("Published flow stats update event to bus", { flowName });
581
- }
582
- } catch (err) {
583
- logger.warn("Failed to publish flow stats update event", {
584
- flowName,
585
- error: err?.message
586
- });
587
- }
588
- } catch (err) {
589
- const errorMsg = err?.message || "";
590
- if (!errorMsg.includes("Entry not found")) {
591
- logger.warn("Failed to update flow stats", {
592
- type: e.type,
593
- flowName: e.flowName,
594
- error: errorMsg
595
- });
596
- } else {
597
- logger.debug("Flow entry not found (will be created)", {
598
- type: e.type,
599
- flowName: e.flowName
600
- });
601
- }
602
- }
603
- };
604
- const handleOrchestration = async (e) => {
605
- try {
606
- if (e.id && e.ts) {
607
- return;
608
- }
609
- if (e.type === "flow.completed" || e.type === "flow.failed") {
610
- return;
611
- }
612
- const runId = e.runId;
613
- if (!runId) return;
614
- const flowName = e.flowName;
615
- if (!flowName) return;
616
- const streamName = StoreSubjects.flowRun(runId);
617
- const indexKey = StoreSubjects.flowRunIndex(flowName);
618
- if (e.type === "flow.start") {
619
- const timestamp = Date.now();
620
- await indexFlowRun(flowName, runId, timestamp, {
621
- status: "running",
622
- startedAt: timestamp,
623
- lastActivityAt: timestamp,
624
- // Initialize for stall detection
625
- stepCount: 0,
626
- completedSteps: 0,
627
- emittedEvents: {}
628
- // Object for atomic updates
629
- });
630
- try {
631
- const analyzedFlows = $useAnalyzedFlows();
632
- const flowMeta = analyzedFlows.find((f) => f.id === flowName);
633
- const stallTimeout = flowMeta?.analyzed?.stallTimeout || 30 * 60 * 1e3;
634
- const scheduler = useScheduler();
635
- const stallJobId = `stall-timeout:${runId}`;
636
- await scheduler.schedule({
637
- id: stallJobId,
638
- name: `Stall Timeout - ${flowName}`,
639
- type: "one-time",
640
- executeAt: timestamp + stallTimeout,
641
- handler: async () => {
642
- if (stallDetector) {
643
- logger.info(`Per-flow stall timeout fired for '${flowName}' runId '${runId}'`);
644
- await stallDetector.markAsStalled(flowName, runId, "Stall timeout reached");
645
- }
646
- },
647
- metadata: {
648
- component: "stall-detector",
649
- flowName,
650
- runId
651
- }
652
- });
653
- logger.debug(`Scheduled stall timeout for flow '${flowName}' runId '${runId}' in ${stallTimeout / 1e3}s`, { jobId: stallJobId });
654
- } catch (error) {
655
- logger.warn(`Failed to schedule stall timeout for flow '${flowName}' runId '${runId}'`, {
656
- error: error.message
657
- });
658
- }
659
- }
660
- if (e.type === "flow.cancel") {
661
- try {
662
- if (store.index.updateWithRetry) {
663
- await store.index.updateWithRetry(indexKey, runId, {
664
- status: "canceled",
665
- completedAt: Date.now()
666
- });
667
- logger.info("Marked flow as canceled", { flowName, runId });
668
- }
669
- const scheduler = useScheduler();
670
- const flowJobs = await scheduler.getJobsByPattern(runId);
671
- for (const job of flowJobs) {
672
- await scheduler.unschedule(job.id);
673
- logger.debug(`Unscheduled job for canceled flow: ${job.id}`);
674
- }
675
- logger.debug(`Unscheduled ${flowJobs.length} scheduled jobs for canceled flow runId '${runId}'`, {
676
- jobs: flowJobs.map((j) => j.id)
677
- });
678
- } catch (err) {
679
- logger.warn("Failed to update canceled status or unschedule flow jobs", {
680
- flowName,
681
- runId,
682
- error: err?.message
683
- });
684
- }
685
- }
686
- if (e.type === "step.started" || e.type === "step.completed" || e.type === "step.failed" || e.type === "step.retry") {
687
- try {
688
- const scheduler = useScheduler();
689
- const stallJobId = `stall-timeout:${runId}`;
690
- const analyzedFlows = $useAnalyzedFlows();
691
- const flowMeta = analyzedFlows.find((f) => f.id === flowName);
692
- const stallTimeout = flowMeta?.analyzed?.stallTimeout || 30 * 60 * 1e3;
693
- await scheduler.unschedule(stallJobId);
694
- await scheduler.schedule({
695
- id: stallJobId,
696
- name: `Stall Timeout - ${flowName}`,
697
- type: "one-time",
698
- executeAt: Date.now() + stallTimeout,
699
- handler: async () => {
700
- if (stallDetector) {
701
- logger.info(`Per-flow stall timeout fired for '${flowName}' runId '${runId}'`);
702
- await stallDetector.markAsStalled(flowName, runId, "Stall timeout reached");
703
- }
704
- },
705
- metadata: {
706
- component: "stall-detector",
707
- flowName,
708
- runId,
709
- stallTimeout
710
- }
711
- });
712
- logger.debug(`Rescheduled stall timeout for flow '${flowName}' runId '${runId}' (activity: ${e.type})`);
713
- } catch (error) {
714
- logger.debug(`Could not reschedule stall timeout for flow '${flowName}' runId '${runId}'`, {
715
- error: error.message
716
- });
717
- }
718
- }
719
- if (e.type === "step.completed") {
720
- try {
721
- if (store.index.increment) {
722
- const newCount = await store.index.increment(indexKey, runId, "completedSteps", 1);
723
- logger.debug("Incremented completedSteps", {
724
- flowName,
725
- runId,
726
- stepName: "stepName" in e ? e.stepName : "unknown",
727
- newCount
728
- });
729
- }
730
- } catch (err) {
731
- logger.warn("Failed to update completedSteps", {
732
- flowName,
733
- runId,
734
- error: err?.message
735
- });
736
- }
737
- }
738
- if (e.type === "await.registered") {
739
- const awaitEvent = e;
740
- const { stepName, awaitType, position, config: config2 } = awaitEvent;
741
- try {
742
- if (store.index.updateWithRetry) {
743
- if (store.index.get) {
744
- const currentEntry = await store.index.get(indexKey, runId);
745
- const currentStatus = currentEntry?.metadata?.status;
746
- if (currentStatus === "canceled") {
747
- logger.debug("Flow already canceled, skipping await registration", { flowName, runId, stepName });
748
- return;
749
- }
750
- }
751
- const now = Date.now();
752
- let timeoutAt;
753
- if (awaitType === "time" && config2.delay) {
754
- timeoutAt = now + config2.delay;
755
- } else if (awaitType === "schedule" && config2.nextOccurrence) {
756
- timeoutAt = config2.nextOccurrence;
757
- } else if (config2.timeout) {
758
- timeoutAt = now + config2.timeout;
759
- }
760
- if (!timeoutAt) {
761
- timeoutAt = now + 24 * 60 * 60 * 1e3;
762
- }
763
- const awaitKey = `${stepName}:${position}`;
764
- const updatePayload = {
765
- status: "awaiting",
766
- // Set flow status to awaiting
767
- awaitingSteps: {
768
- [awaitKey]: {
769
- status: "awaiting",
770
- stepName,
771
- // Keep stepName for queries
772
- awaitType,
773
- position,
774
- config: config2,
775
- registeredAt: now,
776
- timeoutAt
777
- }
778
- }
779
- };
780
- await store.index.updateWithRetry(indexKey, runId, updatePayload);
781
- logger.info("Await registered in index", {
782
- runId,
783
- stepName,
784
- awaitType,
785
- position,
786
- timeoutAt: new Date(timeoutAt).toISOString()
787
- });
788
- }
789
- } catch (err) {
790
- logger.error("Error updating await status", {
791
- runId,
792
- stepName,
793
- error: err?.message
794
- });
795
- }
796
- }
797
- if (e.type === "await.resolved") {
798
- const awaitEvent = e;
799
- const { stepName, triggerData, position } = awaitEvent;
800
- try {
801
- if (store.index.get) {
802
- const currentEntry = await store.index.get(indexKey, runId);
803
- const currentStatus = currentEntry?.metadata?.status;
804
- if (currentStatus === "canceled") {
805
- logger.debug("Flow already canceled, skipping await resolution", { flowName, runId, stepName });
806
- return;
807
- }
808
- }
809
- if (store.index.updateWithRetry) {
810
- const awaitKey = `${stepName}:${position}`;
811
- await store.index.updateWithRetry(indexKey, runId, {
812
- awaitingSteps: {
813
- [awaitKey]: {
814
- status: "resolved",
815
- stepName,
816
- // Keep stepName for queries
817
- triggerData,
818
- position
819
- }
820
- }
821
- });
822
- }
823
- const queue = useQueueAdapter();
824
- const { StoreSubjects: StoreSubjects2 } = useStreamTopics();
825
- const streamName2 = StoreSubjects2.flowRun(runId);
826
- const inputData = {};
827
- if (store.stream.read) {
828
- const events = await store.stream.read(streamName2, { limit: 100 });
829
- const registry2 = $useFunctionRegistry();
830
- const flowRegistry2 = (registry2?.flows || {})[flowName];
831
- const stepMeta2 = flowRegistry2?.steps?.[stepName];
832
- const subscribes = stepMeta2?.subscribes || [];
833
- for (const sub of subscribes) {
834
- const emitEvent = events.find(
835
- (evt) => evt.type === "emit" && evt.data?.name === sub
836
- );
837
- if (emitEvent && emitEvent.data?.payload !== void 0) {
838
- inputData[sub] = emitEvent.data.payload;
839
- }
840
- }
841
- }
842
- const { SYSTEM_HANDLERS: SYSTEM_HANDLERS2 } = await import("../../worker/system/index.js");
843
- const payload = {
844
- flowId: runId,
845
- flowName,
846
- stepName,
847
- position,
848
- triggerData,
849
- input: inputData
850
- };
851
- const jobId = `${runId}__${stepName}__await-resolve`;
852
- const registry = $useFunctionRegistry();
853
- const flowRegistry = (registry?.flows || {})[flowName];
854
- const stepMeta = flowRegistry?.steps?.[stepName];
855
- let stepQueue = stepMeta?.queue;
856
- if (!stepQueue && flowRegistry?.entry?.step === stepName) {
857
- stepQueue = flowRegistry.entry.queue;
858
- }
859
- if (!stepQueue && registry?.workers) {
860
- const worker = registry.workers.find((w) => {
861
- const flowNames = w?.flow?.names || (w?.flow?.name ? [w?.flow?.name] : []);
862
- const stepMatch = w?.flow?.step === stepName || Array.isArray(w?.flow?.step) && w?.flow?.step.includes(stepName);
863
- return flowNames.includes(flowName) && stepMatch;
864
- });
865
- stepQueue = worker?.queue?.name;
866
- }
867
- if (!stepQueue) {
868
- logger.error("Cannot find queue for step", {
869
- stepName,
870
- flowName,
871
- availableSteps: Object.keys(flowRegistry?.steps || {}),
872
- entryStep: flowRegistry?.entry?.step
873
- });
874
- throw new Error(`Cannot resolve await: queue not found for step ${stepName} in flow ${flowName}`);
875
- }
876
- await queue.enqueue(stepQueue, {
877
- name: SYSTEM_HANDLERS2.AWAIT_RESOLVE,
878
- data: payload,
879
- opts: { jobId }
880
- });
881
- await checkAndTriggerPendingSteps(flowName, runId, store);
882
- } catch (err) {
883
- logger.error("Error handling await resolution", {
884
- runId,
885
- stepName,
886
- error: err?.message
887
- });
888
- }
889
- }
890
- if (e.type === "await.timeout") {
891
- const timeoutEvent = e;
892
- const { stepName, timeoutAction, position, awaitType } = timeoutEvent;
893
- const action = timeoutAction || "fail";
894
- if (store.index.get) {
895
- const currentEntry = await store.index.get(indexKey, runId);
896
- const currentStatus = currentEntry?.metadata?.status;
897
- if (currentStatus === "canceled") {
898
- logger.debug("Flow already canceled, skipping await timeout handling", { flowName, runId, stepName });
899
- return;
900
- }
901
- }
902
- logger.warn("Await timeout occurred", {
903
- runId,
904
- stepName,
905
- awaitType,
906
- position,
907
- action
908
- });
909
- try {
910
- const queue = useQueueAdapter();
911
- const { StoreSubjects: StoreSubjects2 } = useStreamTopics();
912
- const streamName2 = StoreSubjects2.flowRun(runId);
913
- const inputData = {};
914
- if (store.stream.read) {
915
- const events = await store.stream.read(streamName2, { limit: 100 });
916
- const registry2 = $useFunctionRegistry();
917
- const flowRegistry2 = (registry2?.flows || {})[flowName];
918
- const stepMeta2 = flowRegistry2?.steps?.[stepName];
919
- const subscribes = stepMeta2?.subscribes || [];
920
- for (const sub of subscribes) {
921
- const emitEvent = events.find(
922
- (evt) => evt.type === "emit" && evt.data?.name === sub
923
- );
924
- if (emitEvent && emitEvent.data?.payload !== void 0) {
925
- inputData[sub] = emitEvent.data.payload;
926
- }
927
- }
928
- }
929
- const payload = {
930
- flowId: runId,
931
- flowName,
932
- stepName,
933
- position,
934
- timeoutAction: action,
935
- input: inputData
936
- };
937
- const jobId = `${runId}__${stepName}__await-timeout`;
938
- const registry = $useFunctionRegistry();
939
- const flowRegistry = (registry?.flows || {})[flowName];
940
- const stepMeta = flowRegistry?.steps?.[stepName];
941
- let stepQueue = stepMeta?.queue;
942
- if (!stepQueue && flowRegistry?.entry?.step === stepName) {
943
- stepQueue = flowRegistry.entry.queue;
944
- }
945
- if (!stepQueue && registry?.workers) {
946
- const worker = registry.workers.find((w) => {
947
- const flowNames = w?.flow?.names || (w?.flow?.name ? [w?.flow?.name] : []);
948
- const stepMatch = w?.flow?.step === stepName || Array.isArray(w?.flow?.step) && w?.flow?.step.includes(stepName);
949
- return flowNames.includes(flowName) && stepMatch;
950
- });
951
- stepQueue = worker?.queue?.name;
952
- }
953
- if (!stepQueue) {
954
- logger.error("Cannot find queue for step", {
955
- stepName,
956
- flowName,
957
- availableSteps: Object.keys(flowRegistry?.steps || {}),
958
- entryStep: flowRegistry?.entry?.step
959
- });
960
- throw new Error(`Cannot handle await timeout: queue not found for step ${stepName} in flow ${flowName}`);
961
- }
962
- await queue.enqueue(stepQueue, {
963
- name: SYSTEM_HANDLERS.AWAIT_TIMEOUT,
964
- data: payload,
965
- opts: { jobId }
966
- });
967
- if (action === "fail") {
968
- if (store.index.updateWithRetry) {
969
- await store.index.updateWithRetry(indexKey, runId, {
970
- awaitingSteps: {
971
- [stepName]: {
972
- status: "timeout",
973
- timedOutAt: Date.now()
974
- }
975
- }
976
- });
977
- }
978
- bus.publish({
979
- type: "step.failed",
980
- runId,
981
- flowName,
982
- stepName,
983
- stepId: `${runId}__${stepName}__timeout`,
984
- attempt: 1,
985
- data: {
986
- error: `Await timeout: ${awaitType} await exceeded timeout`
987
- }
988
- });
989
- } else if (action === "continue") {
990
- if (store.index.updateWithRetry) {
991
- await store.index.updateWithRetry(indexKey, runId, {
992
- awaitingSteps: {
993
- [stepName]: {
994
- status: "resolved",
995
- triggerData: null,
996
- timedOutAt: Date.now()
997
- }
998
- }
999
- });
1000
- }
1001
- logger.info("Await timeout - continuing with null data", { runId, stepName });
1002
- await checkAndTriggerPendingSteps(flowName, runId, store);
1003
- }
1004
- } catch (err) {
1005
- logger.error("Error handling await timeout", {
1006
- runId,
1007
- stepName,
1008
- error: err?.message
1009
- });
1010
- }
1011
- }
1012
- if (e.type === "emit") {
1013
- const eventName = e.data?.name || e.data?.topic;
1014
- if (!eventName) {
1015
- logger.warn("Emit event missing name/topic", { flowName, runId, data: e.data });
1016
- } else {
1017
- try {
1018
- if (!store.index.updateWithRetry) {
1019
- logger.warn("StoreAdapter does not support indexUpdateWithRetry");
1020
- return;
1021
- }
1022
- const timestamp = Date.now();
1023
- const eventParts = eventName.split(".");
1024
- const emittedEventsUpdate = {};
1025
- let current = emittedEventsUpdate;
1026
- for (let i = 0; i < eventParts.length - 1; i++) {
1027
- current[eventParts[i]] = {};
1028
- current = current[eventParts[i]];
1029
- }
1030
- current[eventParts[eventParts.length - 1]] = timestamp;
1031
- const updatePayload = {
1032
- emittedEvents: emittedEventsUpdate
1033
- };
1034
- await store.index.updateWithRetry(indexKey, runId, updatePayload);
1035
- logger.debug("Tracked emit event", {
1036
- flowName,
1037
- runId,
1038
- name: eventName,
1039
- timestamp
1040
- });
1041
- } catch (err) {
1042
- logger.warn("Failed to track emitted event", {
1043
- flowName,
1044
- runId,
1045
- event: eventName,
1046
- error: err?.message
1047
- });
1048
- }
1049
- }
1050
- }
1051
- if (e.type === "step.completed") {
1052
- try {
1053
- await checkAndTriggerPendingSteps(flowName, runId, store);
1054
- } catch (err) {
1055
- logger.error("Error checking pending steps", {
1056
- flowName,
1057
- runId,
1058
- error: err.message
1059
- });
1060
- throw err;
1061
- }
1062
- }
1063
- if (e.type === "step.completed" || e.type === "step.failed") {
1064
- try {
1065
- const allEvents = await store.stream.read(streamName);
1066
- const analyzedFlows = $useAnalyzedFlows();
1067
- const flowDef = analyzedFlows.find((f) => f.id === flowName);
1068
- if (flowDef?.steps) {
1069
- const entryStepName = flowDef.entry?.step;
1070
- const entryStepDef = flowDef.entry;
1071
- const analysis = analyzeFlowCompletion(flowDef.steps, entryStepName, allEvents, entryStepDef);
1072
- const updateMetadata = {
1073
- status: analysis.status,
1074
- stepCount: analysis.totalSteps
1075
- };
1076
- if (analysis.status !== "running" && analysis.completedAt) {
1077
- updateMetadata.completedAt = analysis.completedAt;
1078
- }
1079
- if (store.index.get) {
1080
- const currentEntry = await store.index.get(indexKey, runId);
1081
- const currentStatus = currentEntry?.metadata?.status;
1082
- if (currentStatus === "canceled") {
1083
- logger.debug("Flow already canceled, skipping status update", { flowName, runId });
1084
- return;
1085
- }
1086
- const awaitingStepsObj = currentEntry?.metadata?.awaitingSteps || {};
1087
- let hasActiveAwaits = false;
1088
- let hasTimedOutAwaits = false;
1089
- for (const [_stepName, awaitState] of Object.entries(awaitingStepsObj)) {
1090
- if (awaitState?.status === "awaiting") {
1091
- hasActiveAwaits = true;
1092
- } else if (awaitState?.status === "timeout") {
1093
- hasTimedOutAwaits = true;
1094
- }
1095
- }
1096
- if (hasActiveAwaits) {
1097
- updateMetadata.status = "awaiting";
1098
- }
1099
- if (hasTimedOutAwaits) {
1100
- updateMetadata.status = "failed";
1101
- if (!updateMetadata.completedAt) {
1102
- updateMetadata.completedAt = Date.now();
1103
- }
1104
- }
1105
- }
1106
- if (store.index.updateWithRetry) {
1107
- await store.index.updateWithRetry(indexKey, runId, updateMetadata);
1108
- }
1109
- const finalStatus = updateMetadata.status || analysis.status;
1110
- if (finalStatus === "completed" || finalStatus === "failed") {
1111
- const eventType = finalStatus === "completed" ? "flow.completed" : "flow.failed";
1112
- let currentStatus = null;
1113
- if (store.index.get) {
1114
- const currentEntry = await store.index.get(indexKey, runId);
1115
- currentStatus = currentEntry?.metadata?.status;
1116
- }
1117
- const terminalEventExists = allEvents.some((evt) => evt.type === "flow.completed" || evt.type === "flow.failed");
1118
- const publishKey = `${runId}:terminal`;
1119
- const alreadyPublishing = publishingTerminalEvents.has(publishKey);
1120
- if (terminalEventExists) {
1121
- logger.debug("Flow terminal event already exists in stream, skipping publish", {
1122
- flowName,
1123
- runId,
1124
- currentStatus,
1125
- eventType
1126
- });
1127
- } else if (alreadyPublishing) {
1128
- logger.debug("Flow terminal event already being published, skipping duplicate", {
1129
- flowName,
1130
- runId,
1131
- eventType
1132
- });
1133
- } else {
1134
- publishingTerminalEvents.add(publishKey);
1135
- logger.info("Publishing terminal event to bus", {
1136
- flowName,
1137
- runId,
1138
- eventType
1139
- });
1140
- await bus.publish({
1141
- type: eventType,
1142
- runId,
1143
- flowName,
1144
- data: {}
1145
- });
1146
- }
1147
- }
1148
- }
1149
- } catch (err) {
1150
- logger.warn("Failed to analyze flow completion", {
1151
- flowName,
1152
- runId,
1153
- error: err?.message
1154
- });
1155
- }
1156
- }
1157
- } catch (err) {
1158
- logger.error("ERROR handling event", {
1159
- type: e.type,
1160
- runId: e.runId,
1161
- flowName: e.flowName,
1162
- error: err?.message
1163
- });
1164
- }
1165
- };
1166
- const processEventSequentially = async (event) => {
1167
- const runId = event.runId;
1168
- if (!runId) {
1169
- await handlePersistence(event);
1170
- await handleOrchestration(event);
1171
- await handleFlowStats(event);
1172
- return;
1173
- }
1174
- const previousProcessing = flowProcessingChain.get(runId) || Promise.resolve();
1175
- const currentProcessing = previousProcessing.then(async () => {
1176
- try {
1177
- await handlePersistence(event);
1178
- await handleOrchestration(event);
1179
- await handleFlowStats(event);
1180
- } catch (err) {
1181
- logger.error("Error in sequential event processing", {
1182
- runId,
1183
- type: event.type,
1184
- error: err.message,
1185
- stack: err.stack
1186
- });
1187
- } finally {
1188
- const existingTimer = cleanupTimers.get(runId);
1189
- if (existingTimer) {
1190
- clearTimeout(existingTimer);
1191
- }
1192
- const timer = setTimeout(() => {
1193
- if (flowProcessingChain.get(runId) === currentProcessing) {
1194
- flowProcessingChain.delete(runId);
1195
- cleanupTimers.delete(runId);
1196
- }
1197
- }, 6e4);
1198
- cleanupTimers.set(runId, timer);
1199
- }
1200
- });
1201
- flowProcessingChain.set(runId, currentProcessing);
1202
- return currentProcessing;
1203
- };
1204
- const eventTypes = [
1205
- "flow.start",
1206
- "flow.completed",
1207
- "flow.failed",
1208
- "flow.cancel",
1209
- "step.started",
1210
- "step.completed",
1211
- "step.failed",
1212
- "step.retry",
1213
- "await.registered",
1214
- "await.resolved",
1215
- "await.timeout",
1216
- "log",
1217
- "emit",
1218
- "state"
1219
- ];
1220
- for (const type of eventTypes) {
1221
- unsubs.push(bus.onType(type, processEventSequentially));
1222
- }
1223
- const config = useRuntimeConfig();
1224
- const flowConfig = config.nvent.flow || {};
1225
- stallDetector = createStallDetector(store, flowConfig.stallDetection);
1226
- if (flowConfig.stallDetection?.enabled !== false) {
1227
- await stallDetector.start();
1228
- logger.info("Stall detector initialized - using per-flow scheduler jobs");
1229
- }
1230
- }
1231
- async function stop() {
1232
- const logger = useNventLogger("flow-wiring");
1233
- if (stallDetector) {
1234
- try {
1235
- await stallDetector.stop();
1236
- stallDetector = void 0;
1237
- } catch (error) {
1238
- logger.error("Error stopping stall detector", {
1239
- error: error.message
1240
- });
1241
- }
1242
- }
1243
- for (const u of unsubs.splice(0)) {
1244
- try {
1245
- u();
1246
- } catch {
1247
- }
1248
- }
1249
- publishingTerminalEvents.clear();
1250
- for (const timer of cleanupTimers.values()) {
1251
- clearTimeout(timer);
1252
- }
1253
- cleanupTimers.clear();
1254
- flowProcessingChain.clear();
1255
- wired = false;
1256
- }
1257
- return { start, stop };
1258
- }