nvent 0.4.5 → 0.5.1
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 +1 -1
- package/dist/module.mjs +433 -180
- 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 +15 -1
- package/dist/runtime/adapters/builtin/file-queue.js +70 -6
- package/dist/runtime/adapters/builtin/file-store.d.ts +4 -18
- package/dist/runtime/adapters/builtin/file-store.js +90 -109
- package/dist/runtime/adapters/builtin/memory-queue.js +4 -0
- package/dist/runtime/adapters/builtin/memory-store.d.ts +42 -31
- package/dist/runtime/adapters/builtin/memory-store.js +253 -183
- package/dist/runtime/adapters/factory.d.ts +2 -2
- package/dist/runtime/adapters/factory.js +54 -20
- package/dist/runtime/adapters/interfaces/store.d.ts +177 -113
- package/dist/runtime/config/index.d.ts +2 -2
- package/dist/runtime/config/index.js +14 -6
- package/dist/runtime/config/types.d.ts +32 -2
- package/dist/runtime/events/eventBus.d.ts +1 -1
- package/dist/runtime/events/types.d.ts +31 -2
- 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 +44 -3
- package/dist/runtime/events/utils/stallDetector.js +288 -89
- package/dist/runtime/events/utils/triggerRuntime.d.ts +58 -0
- package/dist/runtime/events/utils/triggerRuntime.js +212 -0
- package/dist/runtime/events/wiring/flowWiring.d.ts +11 -5
- package/dist/runtime/events/wiring/flowWiring.js +620 -92
- package/dist/runtime/events/wiring/registry.d.ts +2 -2
- package/dist/runtime/events/wiring/registry.js +8 -6
- package/dist/runtime/events/wiring/streamWiring.d.ts +15 -11
- package/dist/runtime/events/wiring/streamWiring.js +88 -11
- package/dist/runtime/events/wiring/triggerWiring.d.ts +21 -0
- package/dist/runtime/events/wiring/triggerWiring.js +412 -0
- package/dist/runtime/{server → nitro}/plugins/00.adapters.js +8 -4
- package/dist/runtime/{server → nitro}/plugins/02.workers.js +21 -3
- 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/{utils → nitro/utils}/adapters.d.ts +6 -6
- 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/{utils → nitro/utils}/defineFunction.d.ts +2 -2
- package/dist/runtime/{utils → nitro/utils}/defineFunction.js +3 -3
- package/dist/runtime/{utils → nitro/utils}/defineFunctionConfig.d.ts +156 -0
- package/dist/runtime/{utils → nitro/utils}/defineFunctionConfig.js +1 -0
- package/dist/runtime/nitro/utils/defineHooks.d.ts +41 -0
- package/dist/runtime/nitro/utils/defineHooks.js +6 -0
- package/dist/runtime/{utils → nitro/utils}/registerAdapter.d.ts +3 -3
- package/dist/runtime/{utils → nitro/utils}/registerAdapter.js +1 -1
- package/dist/runtime/nitro/utils/useAwait.d.ts +71 -0
- package/dist/runtime/nitro/utils/useAwait.js +139 -0
- package/dist/runtime/{utils → nitro/utils}/useEventManager.d.ts +2 -2
- package/dist/runtime/{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/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/worker/node/runner.d.ts +12 -2
- package/dist/runtime/worker/node/runner.js +141 -37
- package/package.json +6 -6
- 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 -55
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.js +0 -21
- package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +0 -17
- package/dist/runtime/server/api/_flows/[name]/runs.get.js +0 -64
- package/dist/runtime/server/api/_flows/[name]/schedule.post.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/schedule.post.js +0 -66
- 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 -47
- package/dist/runtime/server/api/_flows/[name]/schedules.get.d.ts +0 -2
- package/dist/runtime/server/api/_flows/[name]/schedules.get.js +0 -50
- 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 -209
- 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 -14
- 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 -27
- package/dist/runtime/server/api/_queues/index.get.d.ts +0 -2
- package/dist/runtime/server/api/_queues/index.get.js +0 -106
- package/dist/runtime/server/api/_queues/ws.d.ts +0 -48
- package/dist/runtime/server/api/_queues/ws.js +0 -215
- package/dist/runtime/utils/useFlowEngine.d.ts +0 -19
- package/dist/runtime/utils/useFlowEngine.js +0 -108
- package/dist/runtime/utils/useStreamTopics.d.ts +0 -72
- package/dist/runtime/utils/useStreamTopics.js +0 -47
- /package/dist/runtime/{server → nitro}/plugins/00.adapters.d.ts +0 -0
- /package/dist/runtime/{server → nitro}/plugins/01.ws-lifecycle.d.ts +0 -0
- /package/dist/runtime/{server → nitro}/plugins/01.ws-lifecycle.js +0 -0
- /package/dist/runtime/{server → nitro}/plugins/02.workers.d.ts +0 -0
- /package/dist/runtime/{utils → nitro/utils}/adapters.js +0 -0
- /package/dist/runtime/{utils → nitro/utils}/useNventLogger.d.ts +0 -0
- /package/dist/runtime/{utils → nitro/utils}/useNventLogger.js +0 -0
- /package/dist/runtime/{utils → nitro/utils}/wsPeerManager.d.ts +0 -0
- /package/dist/runtime/{utils → nitro/utils}/wsPeerManager.js +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduler Types
|
|
3
|
+
*
|
|
4
|
+
* Unified scheduling system for all time-based operations in nvent:
|
|
5
|
+
* - Stall detection
|
|
6
|
+
* - Await timeouts
|
|
7
|
+
* - Trigger schedules
|
|
8
|
+
* - Cleanup jobs
|
|
9
|
+
*/
|
|
10
|
+
export interface ScheduledJob {
|
|
11
|
+
/**
|
|
12
|
+
* Unique job identifier
|
|
13
|
+
*/
|
|
14
|
+
id: string;
|
|
15
|
+
/**
|
|
16
|
+
* Human-readable job name
|
|
17
|
+
*/
|
|
18
|
+
name: string;
|
|
19
|
+
/**
|
|
20
|
+
* Job type
|
|
21
|
+
*/
|
|
22
|
+
type: 'cron' | 'interval' | 'one-time';
|
|
23
|
+
/**
|
|
24
|
+
* Cron expression (for type: 'cron')
|
|
25
|
+
* @example '0 2 * * *' - Daily at 2 AM
|
|
26
|
+
* @example '* /5 * * * *' - Every 5 minutes
|
|
27
|
+
*/
|
|
28
|
+
cron?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Interval in milliseconds (for type: 'interval')
|
|
31
|
+
*/
|
|
32
|
+
interval?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Execution timestamp (for type: 'one-time')
|
|
35
|
+
*/
|
|
36
|
+
executeAt?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Job handler function
|
|
39
|
+
*/
|
|
40
|
+
handler: () => Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Timezone for cron jobs
|
|
43
|
+
* @default 'UTC'
|
|
44
|
+
*/
|
|
45
|
+
timezone?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Whether the job is enabled
|
|
48
|
+
* @default true
|
|
49
|
+
*/
|
|
50
|
+
enabled?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Additional metadata
|
|
53
|
+
*/
|
|
54
|
+
metadata?: Record<string, any>;
|
|
55
|
+
/**
|
|
56
|
+
* Last execution timestamp
|
|
57
|
+
*/
|
|
58
|
+
lastRun?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Next scheduled execution timestamp
|
|
61
|
+
*/
|
|
62
|
+
nextRun?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Total execution count
|
|
65
|
+
*/
|
|
66
|
+
runCount?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Failure count
|
|
69
|
+
*/
|
|
70
|
+
failCount?: number;
|
|
71
|
+
}
|
|
72
|
+
export interface SchedulerAdapter {
|
|
73
|
+
/**
|
|
74
|
+
* Schedule a job with distributed locking support
|
|
75
|
+
*/
|
|
76
|
+
schedule(job: ScheduledJob): Promise<string>;
|
|
77
|
+
/**
|
|
78
|
+
* Unschedule a job
|
|
79
|
+
*/
|
|
80
|
+
unschedule(jobId: string): Promise<boolean>;
|
|
81
|
+
/**
|
|
82
|
+
* Start the scheduler (begins processing scheduled jobs)
|
|
83
|
+
*/
|
|
84
|
+
start(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Stop the scheduler and release all locks
|
|
87
|
+
*/
|
|
88
|
+
stop(): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Get all scheduled jobs (in-memory, for this instance)
|
|
91
|
+
*/
|
|
92
|
+
getScheduledJobs(): Promise<ScheduledJob[]>;
|
|
93
|
+
/**
|
|
94
|
+
* Get all persisted jobs from store (across all instances)
|
|
95
|
+
* Useful for debugging and monitoring in horizontal setups
|
|
96
|
+
*/
|
|
97
|
+
getAllPersistedJobs(): Promise<ScheduledJob[]>;
|
|
98
|
+
/**
|
|
99
|
+
* Check if scheduler is healthy
|
|
100
|
+
*/
|
|
101
|
+
isHealthy(): Promise<boolean>;
|
|
102
|
+
}
|
|
103
|
+
export interface SchedulerLock {
|
|
104
|
+
jobId: string;
|
|
105
|
+
instanceId: string;
|
|
106
|
+
acquiredAt: number;
|
|
107
|
+
expiresAt: number;
|
|
108
|
+
}
|
|
109
|
+
export interface SchedulerStats {
|
|
110
|
+
jobId: string;
|
|
111
|
+
lastRun: number;
|
|
112
|
+
nextRun?: number;
|
|
113
|
+
runCount: number;
|
|
114
|
+
failCount: number;
|
|
115
|
+
lastError?: string;
|
|
116
|
+
}
|
|
File without changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useFlow } from '#imports';
|
|
2
2
|
/**
|
|
3
3
|
* Generic job interface that works with any queue adapter
|
|
4
4
|
* Adapters should provide jobs in this format
|
|
@@ -32,7 +32,17 @@ export interface RunContext {
|
|
|
32
32
|
attempt?: number;
|
|
33
33
|
logger: RunLogger;
|
|
34
34
|
state: RunState;
|
|
35
|
-
flow: ReturnType<typeof
|
|
35
|
+
flow: ReturnType<typeof useFlow>;
|
|
36
|
+
/**
|
|
37
|
+
* Resolved data from await pattern (awaitBefore only)
|
|
38
|
+
* Available when step resumes after await resolution
|
|
39
|
+
*/
|
|
40
|
+
trigger?: any;
|
|
41
|
+
/**
|
|
42
|
+
* Current step's await configuration
|
|
43
|
+
* Useful for conditional logic based on await settings
|
|
44
|
+
*/
|
|
45
|
+
awaitConfig?: any;
|
|
36
46
|
}
|
|
37
47
|
export declare function buildContext(partial?: Partial<RunContext>): RunContext;
|
|
38
48
|
export type NodeHandler = (input: any, ctx: RunContext) => Promise<any>;
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
useRuntimeConfig,
|
|
4
|
+
useFlow,
|
|
5
|
+
useEventManager,
|
|
6
|
+
useNventLogger,
|
|
7
|
+
$useFunctionRegistry,
|
|
8
|
+
useAwait,
|
|
9
|
+
useHookRegistry,
|
|
10
|
+
useStreamTopics,
|
|
11
|
+
useStateAdapter,
|
|
12
|
+
useStoreAdapter,
|
|
13
|
+
useRunContext
|
|
14
|
+
} from "#imports";
|
|
4
15
|
const logger = useNventLogger("node-runner");
|
|
5
16
|
const defaultState = {
|
|
6
17
|
async get() {
|
|
@@ -55,14 +66,15 @@ export function buildContext(partial) {
|
|
|
55
66
|
}
|
|
56
67
|
};
|
|
57
68
|
})();
|
|
58
|
-
const baseFlowEngine =
|
|
69
|
+
const baseFlowEngine = useFlow();
|
|
59
70
|
const flow = {
|
|
60
71
|
...baseFlowEngine,
|
|
61
72
|
emit: async (trigger, payload = {}) => {
|
|
62
73
|
const enrichedPayload = {
|
|
63
74
|
...payload,
|
|
64
75
|
flowId: payload.flowId || partial?.flowId,
|
|
65
|
-
flowName: payload.flowName || partial?.flowName
|
|
76
|
+
flowName: payload.flowName || partial?.flowName,
|
|
77
|
+
stepName: payload.stepName || partial?.stepName
|
|
66
78
|
};
|
|
67
79
|
return baseFlowEngine.emit(trigger, enrichedPayload);
|
|
68
80
|
},
|
|
@@ -97,26 +109,13 @@ export function buildContext(partial) {
|
|
|
97
109
|
attempt: partial?.attempt,
|
|
98
110
|
logger: logger2,
|
|
99
111
|
state,
|
|
100
|
-
flow
|
|
112
|
+
flow,
|
|
113
|
+
trigger: partial?.trigger,
|
|
114
|
+
awaitConfig: partial?.awaitConfig
|
|
101
115
|
};
|
|
102
116
|
}
|
|
103
117
|
export function createJobProcessor(handler, queueName) {
|
|
104
118
|
return async function processor(job) {
|
|
105
|
-
if (job.data?.__scheduledFlowStart) {
|
|
106
|
-
const { __flowName, __flowInput } = job.data;
|
|
107
|
-
try {
|
|
108
|
-
const { startFlow } = useFlowEngine();
|
|
109
|
-
const result2 = await startFlow(__flowName, __flowInput || {});
|
|
110
|
-
return {
|
|
111
|
-
scheduled: true,
|
|
112
|
-
flowId: result2.flowId,
|
|
113
|
-
flowName: __flowName
|
|
114
|
-
};
|
|
115
|
-
} catch (err) {
|
|
116
|
-
logger.error("[scheduled-flow] Failed to start flow:", err);
|
|
117
|
-
throw err;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
119
|
const eventMgr = useEventManager();
|
|
121
120
|
const rc = useRuntimeConfig();
|
|
122
121
|
const autoScope = rc?.nvent?.store?.state?.autoScope || "always";
|
|
@@ -127,6 +126,59 @@ export function createJobProcessor(handler, queueName) {
|
|
|
127
126
|
const isFinalAttempt = attempt >= maxAttempts;
|
|
128
127
|
const stepRunId = `${String(flowId || job.id)}__${job.name}__attempt-${attempt}`;
|
|
129
128
|
const flowName = job.data?.flowName || "unknown";
|
|
129
|
+
const registry = $useFunctionRegistry();
|
|
130
|
+
const flowRegistry = (registry?.flows || {})[flowName];
|
|
131
|
+
let stepMeta = flowRegistry?.steps?.[job.name];
|
|
132
|
+
if (!stepMeta && flowRegistry?.entry?.step === job.name) {
|
|
133
|
+
stepMeta = flowRegistry?.entry;
|
|
134
|
+
}
|
|
135
|
+
const awaitBefore = stepMeta?.awaitBefore;
|
|
136
|
+
const awaitAfter = stepMeta?.awaitAfter;
|
|
137
|
+
const isAwaitResume = job.data?.awaitResolved === true;
|
|
138
|
+
const awaitData = job.data?.awaitData;
|
|
139
|
+
if (awaitBefore && !isAwaitResume) {
|
|
140
|
+
const awaitLogger = useNventLogger("await-before");
|
|
141
|
+
awaitLogger.info("Step has awaitBefore, registering await pattern", {
|
|
142
|
+
flowName,
|
|
143
|
+
runId: flowId,
|
|
144
|
+
stepName: job.name,
|
|
145
|
+
awaitType: awaitBefore.type
|
|
146
|
+
});
|
|
147
|
+
try {
|
|
148
|
+
const { register } = useAwait();
|
|
149
|
+
const awaitResult = await register(
|
|
150
|
+
flowId || "unknown",
|
|
151
|
+
job.name,
|
|
152
|
+
flowName,
|
|
153
|
+
awaitBefore,
|
|
154
|
+
"before"
|
|
155
|
+
// Position: awaitBefore means wait before execution
|
|
156
|
+
);
|
|
157
|
+
const hookRegistry = useHookRegistry();
|
|
158
|
+
const hooks = hookRegistry.load(flowName, job.name);
|
|
159
|
+
if (hooks?.onAwaitRegister) {
|
|
160
|
+
try {
|
|
161
|
+
await hooks.onAwaitRegister(
|
|
162
|
+
awaitResult.webhookUrl || awaitResult.eventName || "",
|
|
163
|
+
job.data,
|
|
164
|
+
useRunContext({ flowId, flowName, stepName: job.name })
|
|
165
|
+
);
|
|
166
|
+
} catch (err) {
|
|
167
|
+
awaitLogger.error("onAwaitRegister hook failed", { error: err.message });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
awaiting: true,
|
|
172
|
+
awaitType: awaitBefore.type,
|
|
173
|
+
awaitConfig: awaitBefore
|
|
174
|
+
};
|
|
175
|
+
} catch (err) {
|
|
176
|
+
awaitLogger.error("Failed to register await pattern", {
|
|
177
|
+
error: err.message,
|
|
178
|
+
stack: err.stack
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
130
182
|
const ctx = buildContext({
|
|
131
183
|
jobId: job.id,
|
|
132
184
|
queue: queueName,
|
|
@@ -134,7 +186,9 @@ export function createJobProcessor(handler, queueName) {
|
|
|
134
186
|
flowName,
|
|
135
187
|
stepName: job.name,
|
|
136
188
|
stepId: stepRunId,
|
|
137
|
-
attempt
|
|
189
|
+
attempt,
|
|
190
|
+
trigger: isAwaitResume ? awaitData : void 0,
|
|
191
|
+
awaitConfig: awaitBefore || awaitAfter || void 0
|
|
138
192
|
});
|
|
139
193
|
const attemptLogger = {
|
|
140
194
|
log: (level, msg, meta) => {
|
|
@@ -168,21 +222,6 @@ export function createJobProcessor(handler, queueName) {
|
|
|
168
222
|
stack: err?.stack
|
|
169
223
|
});
|
|
170
224
|
const willRetry = !isFinalAttempt;
|
|
171
|
-
try {
|
|
172
|
-
await eventMgr.publishBus({
|
|
173
|
-
type: "step.failed",
|
|
174
|
-
runId: flowId || "unknown",
|
|
175
|
-
flowName,
|
|
176
|
-
stepName: job.name,
|
|
177
|
-
stepId: stepRunId,
|
|
178
|
-
attempt,
|
|
179
|
-
data: {
|
|
180
|
-
error: String(err?.message || err),
|
|
181
|
-
stack: err?.stack
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
} catch {
|
|
185
|
-
}
|
|
186
225
|
if (willRetry) {
|
|
187
226
|
try {
|
|
188
227
|
await eventMgr.publishBus({
|
|
@@ -197,7 +236,25 @@ export function createJobProcessor(handler, queueName) {
|
|
|
197
236
|
queue: queueName,
|
|
198
237
|
attempt,
|
|
199
238
|
maxAttempts,
|
|
200
|
-
nextAttempt: attempt + 1
|
|
239
|
+
nextAttempt: attempt + 1,
|
|
240
|
+
error: String(err?.message || err),
|
|
241
|
+
stack: err?.stack
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
} catch {
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
try {
|
|
248
|
+
await eventMgr.publishBus({
|
|
249
|
+
type: "step.failed",
|
|
250
|
+
runId: flowId || "unknown",
|
|
251
|
+
flowName,
|
|
252
|
+
stepName: job.name,
|
|
253
|
+
stepId: stepRunId,
|
|
254
|
+
attempt,
|
|
255
|
+
data: {
|
|
256
|
+
error: String(err?.message || err),
|
|
257
|
+
stack: err?.stack
|
|
201
258
|
}
|
|
202
259
|
});
|
|
203
260
|
} catch {
|
|
@@ -218,6 +275,53 @@ export function createJobProcessor(handler, queueName) {
|
|
|
218
275
|
});
|
|
219
276
|
} catch {
|
|
220
277
|
}
|
|
278
|
+
if (awaitAfter && !isAwaitResume) {
|
|
279
|
+
const awaitLogger = useNventLogger("await-after");
|
|
280
|
+
awaitLogger.info("Step has awaitAfter, registering await pattern", {
|
|
281
|
+
flowName,
|
|
282
|
+
runId: flowId,
|
|
283
|
+
stepName: job.name,
|
|
284
|
+
awaitType: awaitAfter.type
|
|
285
|
+
});
|
|
286
|
+
try {
|
|
287
|
+
const store = useStoreAdapter();
|
|
288
|
+
const { StoreSubjects } = useStreamTopics();
|
|
289
|
+
const streamName = StoreSubjects.flowRun(flowId || "unknown");
|
|
290
|
+
let _emitEvents = [];
|
|
291
|
+
if (store.stream.read) {
|
|
292
|
+
const recentEvents = await store.stream.read(streamName, { limit: 100 });
|
|
293
|
+
_emitEvents = recentEvents.filter(
|
|
294
|
+
(evt) => evt.type === "emit" && evt.stepName === job.name && evt.stepId === stepRunId
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
const { register } = useAwait();
|
|
298
|
+
const awaitResult = await register(
|
|
299
|
+
flowId || "unknown",
|
|
300
|
+
job.name,
|
|
301
|
+
flowName,
|
|
302
|
+
awaitAfter,
|
|
303
|
+
"after"
|
|
304
|
+
);
|
|
305
|
+
const hookRegistry = useHookRegistry();
|
|
306
|
+
const hooks = hookRegistry.load(flowName, job.name);
|
|
307
|
+
if (hooks?.onAwaitRegister) {
|
|
308
|
+
try {
|
|
309
|
+
await hooks.onAwaitRegister(
|
|
310
|
+
awaitResult.webhookUrl || awaitResult.eventName || "",
|
|
311
|
+
{ ...job.data, result },
|
|
312
|
+
ctx
|
|
313
|
+
);
|
|
314
|
+
} catch (err) {
|
|
315
|
+
awaitLogger.error("onAwaitRegister hook failed", { error: err.message });
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} catch (err) {
|
|
319
|
+
awaitLogger.error("Failed to register awaitAfter pattern", {
|
|
320
|
+
error: err.message,
|
|
321
|
+
stack: err.stack
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
221
325
|
return result;
|
|
222
326
|
};
|
|
223
327
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nvent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Event-driven workflows for Nuxt",
|
|
5
5
|
"repository": "DevJoghurt/nvent",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "nuxt-module-build build",
|
|
21
21
|
"dev": "nuxt-module-build build --stub",
|
|
22
|
-
"prepack": "pnpm build"
|
|
23
|
-
"test": "vitest"
|
|
22
|
+
"prepack": "pnpm build"
|
|
24
23
|
},
|
|
25
24
|
"dependencies": {
|
|
26
25
|
"@nuxt/kit": "4.2.1",
|
|
27
|
-
"chokidar": "^
|
|
26
|
+
"chokidar": "^5.0.0",
|
|
27
|
+
"cron": "^4.3.5",
|
|
28
28
|
"cron-parser": "^5.4.0",
|
|
29
29
|
"defu": "^6.1.4",
|
|
30
30
|
"fastq": "^1.19.1",
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
"nuxt": "4.2.1",
|
|
34
34
|
"pathe": "^2.0.3",
|
|
35
35
|
"perfect-debounce": "^2.0.0",
|
|
36
|
-
"zod": "^4.1.
|
|
36
|
+
"zod": "^4.1.13"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@nuxt/module-builder": "^1.0.2",
|
|
40
40
|
"@nuxt/schema": "4.2.1",
|
|
41
41
|
"@types/node": "^24.10.1",
|
|
42
42
|
"typescript": "latest",
|
|
43
|
-
"vitest": "^4.0.
|
|
43
|
+
"vitest": "^4.0.15"
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DELETE /api/_flows/:flowName/clear-history
|
|
3
|
-
*
|
|
4
|
-
* Clears all event history for a specific flow.
|
|
5
|
-
* This deletes:
|
|
6
|
-
* - All flow run streams (nq:flow:run-*)
|
|
7
|
-
* - The flow runs index (nq:flows:<flowName>)
|
|
8
|
-
*/
|
|
9
|
-
declare const _default: any;
|
|
10
|
-
export default _default;
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { defineEventHandler, createError, useStoreAdapter, useStreamTopics } from "#imports";
|
|
2
|
-
export default defineEventHandler(async (event) => {
|
|
3
|
-
const flowName = event.context.params?.name;
|
|
4
|
-
if (!flowName) {
|
|
5
|
-
throw createError({ statusCode: 400, statusMessage: "Flow name required" });
|
|
6
|
-
}
|
|
7
|
-
let store;
|
|
8
|
-
let SubjectPatterns;
|
|
9
|
-
try {
|
|
10
|
-
store = useStoreAdapter();
|
|
11
|
-
const topics = useStreamTopics();
|
|
12
|
-
SubjectPatterns = topics.SubjectPatterns;
|
|
13
|
-
} catch {
|
|
14
|
-
throw createError({
|
|
15
|
-
statusCode: 503,
|
|
16
|
-
statusMessage: "Server initializing",
|
|
17
|
-
data: "Adapters not ready yet, please retry"
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
try {
|
|
21
|
-
let deletedStreams = 0;
|
|
22
|
-
let deletedIndex = false;
|
|
23
|
-
const indexKey = SubjectPatterns.flowRunIndex(flowName);
|
|
24
|
-
if (store.indexRead) {
|
|
25
|
-
const runs = await store.indexRead(indexKey, { limit: 1e4 });
|
|
26
|
-
if (store.deleteStream) {
|
|
27
|
-
for (const run of runs) {
|
|
28
|
-
const streamName = SubjectPatterns.flowRun(run.id);
|
|
29
|
-
await store.deleteStream(streamName);
|
|
30
|
-
if (store.deleteIndex) {
|
|
31
|
-
const metaKey = `${indexKey}:meta:${run.id}`;
|
|
32
|
-
await store.deleteIndex(metaKey);
|
|
33
|
-
}
|
|
34
|
-
deletedStreams++;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (store.deleteIndex) {
|
|
39
|
-
await store.deleteIndex(indexKey);
|
|
40
|
-
deletedIndex = true;
|
|
41
|
-
}
|
|
42
|
-
return {
|
|
43
|
-
success: true,
|
|
44
|
-
flowName,
|
|
45
|
-
deletedStreams,
|
|
46
|
-
deletedIndex
|
|
47
|
-
};
|
|
48
|
-
} catch (error) {
|
|
49
|
-
throw createError({
|
|
50
|
-
statusCode: 500,
|
|
51
|
-
statusMessage: "Failed to clear flow history",
|
|
52
|
-
data: error instanceof Error ? error.message : String(error)
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { defineEventHandler, getRouterParam, createError, useFlowEngine } from "#imports";
|
|
2
|
-
export default defineEventHandler(async (event) => {
|
|
3
|
-
const flowName = getRouterParam(event, "name");
|
|
4
|
-
const runId = getRouterParam(event, "runId");
|
|
5
|
-
if (!flowName || !runId) {
|
|
6
|
-
throw createError({
|
|
7
|
-
statusCode: 400,
|
|
8
|
-
statusMessage: "Flow name and run ID are required"
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
const flowEngine = useFlowEngine();
|
|
12
|
-
try {
|
|
13
|
-
const result = await flowEngine.cancelFlow(flowName, runId);
|
|
14
|
-
return result;
|
|
15
|
-
} catch (error) {
|
|
16
|
-
throw createError({
|
|
17
|
-
statusCode: 500,
|
|
18
|
-
statusMessage: `Failed to cancel flow: ${error.message}`
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GET /api/_flows/:flowName/runs
|
|
3
|
-
*
|
|
4
|
-
* Returns a list of flow runs with their metadata from the sorted set index.
|
|
5
|
-
* Supports pagination via offset/limit query params.
|
|
6
|
-
*
|
|
7
|
-
* Query params:
|
|
8
|
-
* - limit: number (default: 50)
|
|
9
|
-
* - offset: number (default: 0)
|
|
10
|
-
*
|
|
11
|
-
* Returns:
|
|
12
|
-
* {
|
|
13
|
-
* runs: Array<{ id, score, metadata }>
|
|
14
|
-
* }
|
|
15
|
-
*/
|
|
16
|
-
declare const _default: any;
|
|
17
|
-
export default _default;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { defineEventHandler, getRouterParam, getQuery, useStoreAdapter, useNventLogger, useStreamTopics } from "#imports";
|
|
2
|
-
export default defineEventHandler(async (event) => {
|
|
3
|
-
const logger = useNventLogger("api-flows-runs");
|
|
4
|
-
const flowName = getRouterParam(event, "name");
|
|
5
|
-
const query = getQuery(event);
|
|
6
|
-
const limit = Math.min(Number.parseInt(query.limit) || 50, 100);
|
|
7
|
-
const offset = Math.max(Number.parseInt(query.offset) || 0, 0);
|
|
8
|
-
const status = query.status;
|
|
9
|
-
event.node.res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
|
|
10
|
-
event.node.res.setHeader("Pragma", "no-cache");
|
|
11
|
-
if (!flowName) {
|
|
12
|
-
return { error: "Missing flow name" };
|
|
13
|
-
}
|
|
14
|
-
let store;
|
|
15
|
-
let SubjectPatterns;
|
|
16
|
-
try {
|
|
17
|
-
store = useStoreAdapter();
|
|
18
|
-
const topics = useStreamTopics();
|
|
19
|
-
SubjectPatterns = topics.SubjectPatterns;
|
|
20
|
-
} catch (err) {
|
|
21
|
-
logger.error("[flows/runs] Adapters not initialized yet:", { error: err });
|
|
22
|
-
return {
|
|
23
|
-
error: "Server initializing",
|
|
24
|
-
message: "Please retry in a moment"
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
try {
|
|
28
|
-
const runIndexKey = SubjectPatterns.flowRunIndex(flowName);
|
|
29
|
-
let totalCount = 0;
|
|
30
|
-
try {
|
|
31
|
-
const allEntries = await store.indexRead(runIndexKey, { limit: 1e4 });
|
|
32
|
-
totalCount = allEntries.length;
|
|
33
|
-
} catch {
|
|
34
|
-
totalCount = 0;
|
|
35
|
-
}
|
|
36
|
-
const entries = await store.indexRead(runIndexKey, { offset, limit });
|
|
37
|
-
const filteredEntries = status ? entries.filter((e) => e.metadata?.status === status) : entries;
|
|
38
|
-
const items = filteredEntries.map((entry) => ({
|
|
39
|
-
id: entry.id,
|
|
40
|
-
flowName,
|
|
41
|
-
status: entry.metadata?.status || "unknown",
|
|
42
|
-
createdAt: new Date(entry.score).toISOString(),
|
|
43
|
-
startedAt: entry.metadata?.startedAt ? new Date(entry.metadata.startedAt).toISOString() : void 0,
|
|
44
|
-
completedAt: entry.metadata?.completedAt ? new Date(entry.metadata.completedAt).toISOString() : void 0,
|
|
45
|
-
stepCount: entry.metadata?.stepCount || 0,
|
|
46
|
-
completedSteps: entry.metadata?.completedSteps || 0
|
|
47
|
-
}));
|
|
48
|
-
return {
|
|
49
|
-
flowName,
|
|
50
|
-
count: items.length,
|
|
51
|
-
total: totalCount,
|
|
52
|
-
offset,
|
|
53
|
-
limit,
|
|
54
|
-
hasMore: offset + items.length < totalCount,
|
|
55
|
-
items
|
|
56
|
-
};
|
|
57
|
-
} catch (err) {
|
|
58
|
-
logger.error("[flows/runs] error:", { error: err });
|
|
59
|
-
return {
|
|
60
|
-
error: "Failed to list runs",
|
|
61
|
-
message: err instanceof Error ? err.message : String(err)
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { defineEventHandler, readBody, getRouterParam, createError, useQueueAdapter, $useQueueRegistry } from "#imports";
|
|
2
|
-
export default defineEventHandler(async (event) => {
|
|
3
|
-
const flowName = getRouterParam(event, "name");
|
|
4
|
-
if (!flowName) {
|
|
5
|
-
throw createError({ statusCode: 400, statusMessage: "Flow name is required" });
|
|
6
|
-
}
|
|
7
|
-
const body = await readBody(event);
|
|
8
|
-
const { input, cron, delay, jobId, metadata } = body;
|
|
9
|
-
if (cron && delay) {
|
|
10
|
-
throw createError({
|
|
11
|
-
statusCode: 400,
|
|
12
|
-
statusMessage: "Cannot specify both cron and delay"
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
if (!cron && !delay) {
|
|
16
|
-
throw createError({
|
|
17
|
-
statusCode: 400,
|
|
18
|
-
statusMessage: "Must specify either cron or delay"
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
const registry = $useQueueRegistry();
|
|
22
|
-
const flow = registry?.flows?.[flowName];
|
|
23
|
-
if (!flow || !flow.entry) {
|
|
24
|
-
throw createError({ statusCode: 404, statusMessage: `Flow '${flowName}' not found` });
|
|
25
|
-
}
|
|
26
|
-
const queueName = typeof flow.entry.queue === "string" ? flow.entry.queue : flow.entry.queue?.name || flow.entry.queue;
|
|
27
|
-
let queue;
|
|
28
|
-
try {
|
|
29
|
-
queue = useQueueAdapter();
|
|
30
|
-
} catch {
|
|
31
|
-
throw createError({
|
|
32
|
-
statusCode: 503,
|
|
33
|
-
statusMessage: "Server initializing",
|
|
34
|
-
data: "Queue adapter not ready yet, please retry"
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
const scheduleOpts = {};
|
|
38
|
-
if (cron) scheduleOpts.cron = cron;
|
|
39
|
-
if (delay) scheduleOpts.delay = delay;
|
|
40
|
-
const jobData = {
|
|
41
|
-
__scheduledFlowStart: true,
|
|
42
|
-
__flowName: flowName,
|
|
43
|
-
__flowInput: input || {},
|
|
44
|
-
__schedule: {
|
|
45
|
-
metadata,
|
|
46
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
const id = await queue.schedule(
|
|
50
|
-
queueName,
|
|
51
|
-
{
|
|
52
|
-
name: flow.entry.step,
|
|
53
|
-
data: jobData,
|
|
54
|
-
opts: jobId ? { jobId } : void 0
|
|
55
|
-
},
|
|
56
|
-
scheduleOpts
|
|
57
|
-
);
|
|
58
|
-
return {
|
|
59
|
-
id,
|
|
60
|
-
flowName,
|
|
61
|
-
queue: queueName,
|
|
62
|
-
step: flow.entry.step,
|
|
63
|
-
schedule: scheduleOpts,
|
|
64
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
65
|
-
};
|
|
66
|
-
});
|