nvent 0.5.4 → 0.5.6
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.json +1 -1
- package/dist/module.mjs +127 -23
- package/dist/runtime/adapters/builtin/memory-store.d.ts +2 -1
- package/dist/runtime/adapters/builtin/memory-store.js +28 -4
- package/dist/runtime/adapters/factory.js +8 -7
- package/dist/runtime/adapters/interfaces/queue.d.ts +5 -0
- package/dist/runtime/adapters/interfaces/store.d.ts +3 -1
- package/dist/runtime/config/index.js +14 -1
- package/dist/runtime/config/types.d.ts +42 -0
- package/dist/runtime/events/types.d.ts +0 -1
- package/dist/runtime/events/utils/stallDetector.d.ts +13 -77
- package/dist/runtime/events/utils/stallDetector.js +8 -192
- package/dist/runtime/events/wiring/flowWiring.js +347 -109
- package/dist/runtime/events/wiring/registry.js +9 -1
- package/dist/runtime/events/wiring/triggerWiring.js +11 -1
- package/dist/runtime/nitro/plugins/02.workers.js +31 -2
- package/dist/runtime/nitro/routes/webhook.await.js +28 -6
- package/dist/runtime/nitro/routes/webhook.trigger.d.ts +17 -0
- package/dist/runtime/nitro/routes/webhook.trigger.js +9 -0
- package/dist/runtime/nitro/utils/awaitPatterns/event.js +58 -50
- package/dist/runtime/nitro/utils/awaitPatterns/schedule.js +6 -1
- package/dist/runtime/nitro/utils/awaitPatterns/time.d.ts +1 -1
- package/dist/runtime/nitro/utils/awaitPatterns/time.js +6 -2
- package/dist/runtime/nitro/utils/awaitPatterns/webhook.js +53 -45
- package/dist/runtime/nitro/utils/defineFunction.d.ts +2 -9
- package/dist/runtime/nitro/utils/defineFunction.js +1 -14
- package/dist/runtime/nitro/utils/defineFunctionConfig.d.ts +84 -16
- package/dist/runtime/nitro/utils/defineHooks.d.ts +64 -10
- package/dist/runtime/nitro/utils/defineHooks.js +3 -0
- package/dist/runtime/nitro/utils/useAwait.d.ts +12 -0
- package/dist/runtime/nitro/utils/useAwait.js +34 -4
- package/dist/runtime/nitro/utils/useFlow.d.ts +39 -48
- package/dist/runtime/nitro/utils/useFlow.js +53 -14
- package/dist/runtime/nitro/utils/useHookRegistry.d.ts +10 -4
- package/dist/runtime/nitro/utils/useTrigger.js +7 -16
- package/dist/runtime/scheduler/index.js +5 -1
- package/dist/runtime/scheduler/scheduler.d.ts +19 -0
- package/dist/runtime/scheduler/scheduler.js +184 -8
- package/dist/runtime/scheduler/types.d.ts +6 -0
- package/dist/runtime/worker/node/runner.d.ts +44 -2
- package/dist/runtime/worker/node/runner.js +45 -100
- package/dist/runtime/worker/system/awaitHandlers.d.ts +27 -0
- package/dist/runtime/worker/system/awaitHandlers.js +230 -0
- package/dist/runtime/worker/system/index.d.ts +24 -0
- package/dist/runtime/worker/system/index.js +39 -0
- package/package.json +1 -1
|
@@ -110,6 +110,14 @@ export interface FlowConfig {
|
|
|
110
110
|
*/
|
|
111
111
|
mode?: 'auto' | 'manual';
|
|
112
112
|
};
|
|
113
|
+
/**
|
|
114
|
+
* Step execution timeout in milliseconds (v0.5.1)
|
|
115
|
+
* Overrides global flow.stepTimeout and queue.defaultJobOptions.timeout for this specific step
|
|
116
|
+
*
|
|
117
|
+
* @example 600000 // 10 minutes
|
|
118
|
+
* @example 3600000 // 1 hour
|
|
119
|
+
*/
|
|
120
|
+
stepTimeout?: number;
|
|
113
121
|
/**
|
|
114
122
|
* Await pattern: Wait BEFORE step execution (v0.5)
|
|
115
123
|
* Step won't execute until trigger fires
|
|
@@ -123,53 +131,113 @@ export interface FlowConfig {
|
|
|
123
131
|
}
|
|
124
132
|
/**
|
|
125
133
|
* Await configuration (run-scoped triggers)
|
|
126
|
-
*
|
|
134
|
+
*
|
|
135
|
+
* Pauses flow execution until a specific condition is met. Can be used with:
|
|
136
|
+
* - `awaitBefore`: Wait before step execution starts
|
|
137
|
+
* - `awaitAfter`: Wait after step completes before triggering next steps
|
|
138
|
+
*
|
|
139
|
+
* Declared in config, no functions allowed (AST-parsed at build time)
|
|
127
140
|
*/
|
|
128
141
|
export interface AwaitConfig {
|
|
129
142
|
/**
|
|
130
|
-
*
|
|
143
|
+
* Type of trigger to wait for
|
|
144
|
+
* - `webhook`: Wait for HTTP request to specific endpoint
|
|
145
|
+
* - `event`: Wait for custom event with optional data matching
|
|
146
|
+
* - `schedule`: Wait until specific cron schedule time
|
|
147
|
+
* - `time`: Wait for fixed time delay
|
|
131
148
|
*/
|
|
132
149
|
type: 'webhook' | 'event' | 'schedule' | 'time';
|
|
133
150
|
/**
|
|
134
|
-
* URL path for webhook (supports template variables
|
|
151
|
+
* URL path for webhook trigger (supports template variables)
|
|
152
|
+
*
|
|
153
|
+
* Only used when `type: 'webhook'`
|
|
154
|
+
*
|
|
155
|
+
* Template variables available:
|
|
156
|
+
* - `{runId}`: Current flow run ID
|
|
157
|
+
* - `{stepId}`: Current step execution ID
|
|
158
|
+
*
|
|
159
|
+
* @example '/webhooks/approve/{runId}'
|
|
160
|
+
* @example '/api/confirm/{stepId}'
|
|
135
161
|
*/
|
|
136
162
|
path?: string;
|
|
137
163
|
/**
|
|
138
|
-
* HTTP method for webhook
|
|
164
|
+
* HTTP method for webhook endpoint
|
|
165
|
+
*
|
|
166
|
+
* Only used when `type: 'webhook'`
|
|
167
|
+
*
|
|
168
|
+
* @default 'POST'
|
|
139
169
|
*/
|
|
140
170
|
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
141
171
|
/**
|
|
142
172
|
* Event name to wait for
|
|
173
|
+
*
|
|
174
|
+
* Only used when `type: 'event'`
|
|
175
|
+
*
|
|
176
|
+
* @example 'payment.completed'
|
|
177
|
+
* @example 'order.shipped'
|
|
143
178
|
*/
|
|
144
179
|
event?: string;
|
|
145
180
|
/**
|
|
146
|
-
* Key to match between event and step
|
|
147
|
-
*
|
|
148
|
-
*
|
|
181
|
+
* Key to match between event data and step output
|
|
182
|
+
*
|
|
183
|
+
* Only used when `type: 'event'`
|
|
184
|
+
*
|
|
185
|
+
* Matches incoming event data against step output data.
|
|
186
|
+
* Supports nested paths using dot notation.
|
|
187
|
+
*
|
|
188
|
+
* @example 'orderId' - matches event.orderId === stepOutput.orderId
|
|
189
|
+
* @example 'order.id' - matches event.order.id === stepOutput.order.id
|
|
149
190
|
*/
|
|
150
191
|
filterKey?: string;
|
|
151
192
|
/**
|
|
152
|
-
* Cron expression
|
|
193
|
+
* Cron expression defining when to trigger (one-time schedule)
|
|
194
|
+
*
|
|
195
|
+
* Only used when `type: 'schedule'`
|
|
196
|
+
*
|
|
197
|
+
* Note: This is a one-time trigger, not recurring. The next occurrence
|
|
198
|
+
* of the cron expression after step completion will trigger continuation.
|
|
199
|
+
*
|
|
200
|
+
* @example '0 9 * * *' - 9 AM daily (triggers at next 9 AM occurrence)
|
|
201
|
+
* @example '0 0 * * MON' - Midnight every Monday
|
|
153
202
|
*/
|
|
154
203
|
cron?: string;
|
|
155
204
|
/**
|
|
156
|
-
*
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
*
|
|
205
|
+
* Timezone for cron expression evaluation
|
|
206
|
+
*
|
|
207
|
+
* Only used when `type: 'schedule'`
|
|
208
|
+
*
|
|
209
|
+
* @example 'America/New_York'
|
|
210
|
+
* @example 'Europe/Berlin'
|
|
211
|
+
* @default 'UTC'
|
|
161
212
|
*/
|
|
162
213
|
timezone?: string;
|
|
163
214
|
/**
|
|
164
|
-
*
|
|
215
|
+
* Fixed delay in milliseconds before continuation
|
|
216
|
+
*
|
|
217
|
+
* Only used when `type: 'time'`
|
|
218
|
+
*
|
|
219
|
+
* @example 60000 - 1 minute
|
|
220
|
+
* @example 3600000 - 1 hour
|
|
165
221
|
*/
|
|
166
222
|
delay?: number;
|
|
167
223
|
/**
|
|
168
|
-
* Maximum wait time in milliseconds
|
|
224
|
+
* Maximum wait time in milliseconds before timeout
|
|
225
|
+
*
|
|
226
|
+
* Applies to all trigger types. If the trigger doesn't fire within
|
|
227
|
+
* this duration, the timeout action will be taken.
|
|
228
|
+
*
|
|
229
|
+
* @example 86400000 - 24 hours
|
|
230
|
+
* @example 604800000 - 7 days
|
|
169
231
|
*/
|
|
170
232
|
timeout?: number;
|
|
171
233
|
/**
|
|
172
|
-
* Action to take
|
|
234
|
+
* Action to take when timeout is reached
|
|
235
|
+
*
|
|
236
|
+
* - `fail`: Mark step/flow as failed
|
|
237
|
+
* - `continue`: Continue flow execution anyway
|
|
238
|
+
* - `retry`: Retry waiting (reset timeout)
|
|
239
|
+
*
|
|
240
|
+
* @default 'fail'
|
|
173
241
|
*/
|
|
174
242
|
timeoutAction?: 'fail' | 'continue' | 'retry';
|
|
175
243
|
}
|
|
@@ -2,30 +2,65 @@
|
|
|
2
2
|
* Type-safe hook definitions for await patterns
|
|
3
3
|
* v0.5 - Await Integration
|
|
4
4
|
*/
|
|
5
|
-
import type { RunContext } from '
|
|
6
|
-
export
|
|
7
|
-
|
|
5
|
+
import type { RunContext } from '../../worker/node/runner.js';
|
|
6
|
+
export type AwaitType = 'webhook' | 'event' | 'schedule' | 'time';
|
|
7
|
+
/**
|
|
8
|
+
* Hook data types specific to each await type
|
|
9
|
+
*/
|
|
10
|
+
export interface WebhookHookData {
|
|
11
|
+
webhookUrl: string;
|
|
12
|
+
}
|
|
13
|
+
export interface EventHookData {
|
|
14
|
+
eventName: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ScheduleHookData {
|
|
17
|
+
cronExpression: string;
|
|
18
|
+
nextOccurrence: Date;
|
|
19
|
+
}
|
|
20
|
+
export interface TimeHookData {
|
|
21
|
+
delayMs: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Conditional type to get the correct hook data based on await type
|
|
25
|
+
*/
|
|
26
|
+
export type HookDataForAwaitType<T extends AwaitType> = T extends 'webhook' ? WebhookHookData : T extends 'event' ? EventHookData : T extends 'schedule' ? ScheduleHookData : T extends 'time' ? TimeHookData : never;
|
|
27
|
+
export interface AwaitRegisterContext<T extends AwaitType = AwaitType> extends Pick<RunContext, 'flowId' | 'flowName' | 'stepName' | 'logger' | 'state'> {
|
|
28
|
+
awaitType: T;
|
|
8
29
|
awaitConfig: any;
|
|
30
|
+
position: 'before' | 'after';
|
|
9
31
|
}
|
|
10
|
-
export interface AwaitResolveContext extends Pick<RunContext, 'flowId' | 'flowName' | 'stepName' | 'logger' | 'state'> {
|
|
11
|
-
awaitType:
|
|
32
|
+
export interface AwaitResolveContext<T extends AwaitType = AwaitType> extends Pick<RunContext, 'flowId' | 'flowName' | 'stepName' | 'logger' | 'state'> {
|
|
33
|
+
awaitType: T;
|
|
12
34
|
resolvedData: any;
|
|
35
|
+
position: 'before' | 'after';
|
|
36
|
+
}
|
|
37
|
+
export interface AwaitTimeoutContext<T extends AwaitType = AwaitType> extends Pick<RunContext, 'flowId' | 'flowName' | 'stepName' | 'logger' | 'state'> {
|
|
38
|
+
awaitType: T;
|
|
39
|
+
timeoutAction: 'fail' | 'continue' | 'retry';
|
|
40
|
+
position: 'before' | 'after';
|
|
13
41
|
}
|
|
14
42
|
/**
|
|
15
43
|
* Define onAwaitRegister hook with proper types
|
|
16
44
|
* Called when an await pattern is registered (before handler execution or after completion)
|
|
17
45
|
*
|
|
18
46
|
* @example
|
|
19
|
-
*
|
|
20
|
-
*
|
|
47
|
+
* // Webhook example
|
|
48
|
+
* export const onAwaitRegister = defineAwaitRegisterHook(async (hookData, stepData, ctx) => {
|
|
49
|
+
* // hookData.webhookUrl is typed correctly for webhook awaits
|
|
21
50
|
* await sendEmail({
|
|
22
51
|
* to: stepData.reviewerEmail,
|
|
23
52
|
* subject: 'Approval Required',
|
|
24
|
-
* approveUrl: webhookUrl
|
|
53
|
+
* approveUrl: hookData.webhookUrl
|
|
25
54
|
* })
|
|
26
55
|
* })
|
|
56
|
+
*
|
|
57
|
+
* // Event example
|
|
58
|
+
* export const onAwaitRegister = defineAwaitRegisterHook(async (hookData, stepData, ctx) => {
|
|
59
|
+
* // hookData.eventName is available for event awaits
|
|
60
|
+
* console.log(`Waiting for event: ${hookData.eventName}`)
|
|
61
|
+
* })
|
|
27
62
|
*/
|
|
28
|
-
export declare function defineAwaitRegisterHook(hook: (
|
|
63
|
+
export declare function defineAwaitRegisterHook<T extends AwaitType = AwaitType>(hook: (hookData: HookDataForAwaitType<T>, stepData: any, ctx: AwaitRegisterContext<T>) => Promise<void>): (hookData: HookDataForAwaitType<T>, stepData: any, ctx: AwaitRegisterContext<T>) => Promise<void>;
|
|
29
64
|
/**
|
|
30
65
|
* Define onAwaitResolve hook with proper types
|
|
31
66
|
* Called when an await pattern is resolved (external trigger fired)
|
|
@@ -38,4 +73,23 @@ export declare function defineAwaitRegisterHook(hook: (webhookUrl: string, stepD
|
|
|
38
73
|
* await updateTicketStatus(stepData.ticketId, resolvedData.approved ? 'approved' : 'rejected')
|
|
39
74
|
* })
|
|
40
75
|
*/
|
|
41
|
-
export declare function defineAwaitResolveHook(hook: (resolvedData: any, stepData: any, ctx: AwaitResolveContext) => Promise<void>): (resolvedData: any, stepData: any, ctx: AwaitResolveContext) => Promise<void>;
|
|
76
|
+
export declare function defineAwaitResolveHook<T extends AwaitType = AwaitType>(hook: (resolvedData: any, stepData: any, ctx: AwaitResolveContext<T>) => Promise<void>): (resolvedData: any, stepData: any, ctx: AwaitResolveContext<T>) => Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Define onAwaitTimeout hook with proper types
|
|
79
|
+
* Called when an await pattern times out (timeout exceeded without resolution)
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* export const onAwaitTimeout = defineAwaitTimeoutHook(async (stepData, ctx) => {
|
|
83
|
+
* ctx.logger.log('warn', 'Approval request timed out', {
|
|
84
|
+
* action: ctx.timeoutAction
|
|
85
|
+
* })
|
|
86
|
+
*
|
|
87
|
+
* // Send timeout notification
|
|
88
|
+
* await sendEmail({
|
|
89
|
+
* to: stepData.reviewerEmail,
|
|
90
|
+
* subject: 'Approval Request Expired',
|
|
91
|
+
* message: 'The approval request was not completed in time'
|
|
92
|
+
* })
|
|
93
|
+
* })
|
|
94
|
+
*/
|
|
95
|
+
export declare function defineAwaitTimeoutHook<T extends AwaitType = AwaitType>(hook: (stepData: any, ctx: AwaitTimeoutContext<T>) => Promise<void>): (stepData: any, ctx: AwaitTimeoutContext<T>) => Promise<void>;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import type { AwaitConfig } from '../../../registry/types.js';
|
|
2
2
|
import { registerAwaitPattern, resolveAwaitPattern, registerWebhookAwait, resolveWebhookAwait, registerEventAwait, resolveEventAwait, registerScheduleAwait, resolveScheduleAwait, registerTimeAwait, resolveTimeAwait } from './awaitPatterns/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Get default timeout values for await patterns from runtime config
|
|
5
|
+
* These are configurable via module options and used throughout the system
|
|
6
|
+
* Exported for use by await pattern implementations
|
|
7
|
+
*/
|
|
8
|
+
export declare function useAwaitDefaults(): {
|
|
9
|
+
webhookTimeout: any;
|
|
10
|
+
eventTimeout: any;
|
|
11
|
+
timeTimeout: any;
|
|
12
|
+
scheduleTimeout: any;
|
|
13
|
+
timeoutAction: any;
|
|
14
|
+
};
|
|
3
15
|
/**
|
|
4
16
|
* Await pattern composable
|
|
5
17
|
* Provides unified API for managing await patterns in flows
|
|
@@ -10,7 +10,34 @@ import {
|
|
|
10
10
|
registerTimeAwait,
|
|
11
11
|
resolveTimeAwait
|
|
12
12
|
} from "./awaitPatterns/index.js";
|
|
13
|
-
import { useStoreAdapter, useStreamTopics, useNventLogger } from "#imports";
|
|
13
|
+
import { useStoreAdapter, useStreamTopics, useNventLogger, useRuntimeConfig } from "#imports";
|
|
14
|
+
export function useAwaitDefaults() {
|
|
15
|
+
try {
|
|
16
|
+
const config = useRuntimeConfig();
|
|
17
|
+
const awaitDefaults = config?.nvent?.flow?.awaitDefaults;
|
|
18
|
+
return {
|
|
19
|
+
webhookTimeout: awaitDefaults?.webhookTimeout ?? 24 * 60 * 60 * 1e3,
|
|
20
|
+
// 24 hours
|
|
21
|
+
eventTimeout: awaitDefaults?.eventTimeout ?? 24 * 60 * 60 * 1e3,
|
|
22
|
+
// 24 hours
|
|
23
|
+
timeTimeout: awaitDefaults?.timeTimeout,
|
|
24
|
+
// undefined by default
|
|
25
|
+
scheduleTimeout: awaitDefaults?.scheduleTimeout,
|
|
26
|
+
// undefined by default
|
|
27
|
+
timeoutAction: awaitDefaults?.timeoutAction ?? "fail"
|
|
28
|
+
};
|
|
29
|
+
} catch {
|
|
30
|
+
return {
|
|
31
|
+
webhookTimeout: 24 * 60 * 60 * 1e3,
|
|
32
|
+
// 24 hours
|
|
33
|
+
eventTimeout: 24 * 60 * 60 * 1e3,
|
|
34
|
+
// 24 hours
|
|
35
|
+
timeTimeout: void 0,
|
|
36
|
+
scheduleTimeout: void 0,
|
|
37
|
+
timeoutAction: "fail"
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
14
41
|
export function useAwait() {
|
|
15
42
|
const logger = useNventLogger("await");
|
|
16
43
|
const store = useStoreAdapter();
|
|
@@ -62,7 +89,10 @@ export function useAwait() {
|
|
|
62
89
|
}
|
|
63
90
|
const awaitingSteps = entry.metadata.awaitingSteps || {};
|
|
64
91
|
if (stepName) {
|
|
65
|
-
|
|
92
|
+
const awaitKeyBefore = `${stepName}:before`;
|
|
93
|
+
const awaitKeyAfter = `${stepName}:after`;
|
|
94
|
+
const awaitState = awaitingSteps[awaitKeyBefore] || awaitingSteps[awaitKeyAfter] || awaitingSteps[stepName];
|
|
95
|
+
return awaitState || null;
|
|
66
96
|
}
|
|
67
97
|
return awaitingSteps;
|
|
68
98
|
},
|
|
@@ -78,8 +108,8 @@ export function useAwait() {
|
|
|
78
108
|
*/
|
|
79
109
|
async getAllActiveAwaits(flowName) {
|
|
80
110
|
const activeAwaits = [];
|
|
81
|
-
if (!store.
|
|
82
|
-
logger.warn("Store does not support
|
|
111
|
+
if (!store.index.read) {
|
|
112
|
+
logger.warn("Store does not support index read");
|
|
83
113
|
return activeAwaits;
|
|
84
114
|
}
|
|
85
115
|
if (flowName) {
|
|
@@ -14,55 +14,46 @@ export interface FlowStats {
|
|
|
14
14
|
};
|
|
15
15
|
version?: number;
|
|
16
16
|
}
|
|
17
|
+
export interface StartFlowResult {
|
|
18
|
+
id: string;
|
|
19
|
+
queue: string;
|
|
20
|
+
step: string;
|
|
21
|
+
flowId: string;
|
|
22
|
+
}
|
|
23
|
+
export interface CancelFlowResult {
|
|
24
|
+
success: boolean;
|
|
25
|
+
runId: string;
|
|
26
|
+
flowName: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RunningFlow {
|
|
29
|
+
id: string;
|
|
30
|
+
flowName: string;
|
|
31
|
+
status: string;
|
|
32
|
+
startedAt: string | undefined;
|
|
33
|
+
stepCount: number;
|
|
34
|
+
completedSteps: number;
|
|
35
|
+
}
|
|
36
|
+
export interface FlowComposable {
|
|
37
|
+
startFlow: (flowName: string, payload?: any) => Promise<StartFlowResult>;
|
|
38
|
+
emit: (trigger: string, payload?: any) => Promise<any[]>;
|
|
39
|
+
cancelFlow: (flowName: string, runId: string) => Promise<CancelFlowResult>;
|
|
40
|
+
isRunning: (flowName: string, runId?: string, options?: {
|
|
41
|
+
excludeRunIds?: string[];
|
|
42
|
+
}) => Promise<boolean>;
|
|
43
|
+
getRunningFlows: (flowName: string, options?: {
|
|
44
|
+
excludeRunIds?: string[];
|
|
45
|
+
}) => Promise<RunningFlow[]>;
|
|
46
|
+
getFlowStats: (flowName: string) => Promise<FlowStats | null>;
|
|
47
|
+
getAllFlowStats: (options?: {
|
|
48
|
+
sortBy?: 'registeredAt' | 'lastRunAt' | 'name';
|
|
49
|
+
order?: 'asc' | 'desc';
|
|
50
|
+
limit?: number;
|
|
51
|
+
offset?: number;
|
|
52
|
+
}) => Promise<FlowStats[]>;
|
|
53
|
+
hasFlowStats: (flowName: string) => Promise<boolean>;
|
|
54
|
+
}
|
|
17
55
|
/**
|
|
18
56
|
* Flow composable for managing flows and accessing flow statistics
|
|
19
57
|
* Provides methods for starting, canceling, and querying flows
|
|
20
58
|
*/
|
|
21
|
-
export declare function useFlow():
|
|
22
|
-
/**
|
|
23
|
-
* Start a flow with the given payload
|
|
24
|
-
*/
|
|
25
|
-
startFlow(flowName: string, payload?: any): Promise<{
|
|
26
|
-
id: any;
|
|
27
|
-
queue: any;
|
|
28
|
-
step: any;
|
|
29
|
-
flowId: `${string}-${string}-${string}-${string}-${string}`;
|
|
30
|
-
}>;
|
|
31
|
-
/**
|
|
32
|
-
* Emit a trigger event for flow coordination
|
|
33
|
-
*/
|
|
34
|
-
emit(trigger: string, payload?: any): Promise<never[]>;
|
|
35
|
-
/**
|
|
36
|
-
* Cancel a running flow
|
|
37
|
-
*/
|
|
38
|
-
cancelFlow(flowName: string, runId: string): Promise<{
|
|
39
|
-
success: boolean;
|
|
40
|
-
runId: string;
|
|
41
|
-
flowName: string;
|
|
42
|
-
}>;
|
|
43
|
-
/**
|
|
44
|
-
* Check if a flow is currently running
|
|
45
|
-
*/
|
|
46
|
-
isRunning(flowName: string, runId?: string): Promise<any>;
|
|
47
|
-
/**
|
|
48
|
-
* Get all currently running flows
|
|
49
|
-
*/
|
|
50
|
-
getRunningFlows(flowName: string): Promise<any>;
|
|
51
|
-
/**
|
|
52
|
-
* Get flow statistics by name
|
|
53
|
-
*/
|
|
54
|
-
getFlowStats(flowName: string): Promise<FlowStats | null>;
|
|
55
|
-
/**
|
|
56
|
-
* Get all flows with their statistics
|
|
57
|
-
*/
|
|
58
|
-
getAllFlowStats(options?: {
|
|
59
|
-
sortBy?: "registeredAt" | "lastRunAt" | "name";
|
|
60
|
-
order?: "asc" | "desc";
|
|
61
|
-
limit?: number;
|
|
62
|
-
offset?: number;
|
|
63
|
-
}): Promise<FlowStats[]>;
|
|
64
|
-
/**
|
|
65
|
-
* Check if a flow has statistics in the index
|
|
66
|
-
*/
|
|
67
|
-
hasFlowStats(flowName: string): Promise<boolean>;
|
|
68
|
-
};
|
|
59
|
+
export declare function useFlow(): FlowComposable;
|
|
@@ -20,9 +20,20 @@ export function useFlow() {
|
|
|
20
20
|
);
|
|
21
21
|
const opts = entryWorker?.queue?.defaultJobOptions || {};
|
|
22
22
|
const flowId = randomUUID();
|
|
23
|
-
const id = await queueAdapter.enqueue(queueName, {
|
|
23
|
+
const id = await queueAdapter.enqueue(queueName, {
|
|
24
|
+
name: flow.entry.step,
|
|
25
|
+
data: { ...payload, flowId, flowName },
|
|
26
|
+
opts
|
|
27
|
+
});
|
|
24
28
|
try {
|
|
25
|
-
await eventsManager.publishBus({
|
|
29
|
+
await eventsManager.publishBus({
|
|
30
|
+
type: "flow.start",
|
|
31
|
+
runId: flowId,
|
|
32
|
+
flowName,
|
|
33
|
+
data: {
|
|
34
|
+
input: payload
|
|
35
|
+
}
|
|
36
|
+
});
|
|
26
37
|
} catch {
|
|
27
38
|
}
|
|
28
39
|
return { id, queue: queueName, step: flow.entry.step, flowId };
|
|
@@ -59,15 +70,24 @@ export function useFlow() {
|
|
|
59
70
|
*/
|
|
60
71
|
async cancelFlow(flowName, runId) {
|
|
61
72
|
try {
|
|
73
|
+
let previousStatus;
|
|
74
|
+
try {
|
|
75
|
+
const runIndexKey = StoreSubjects.flowRunIndex(flowName);
|
|
76
|
+
const entry = await store.index.get(runIndexKey, runId);
|
|
77
|
+
previousStatus = entry?.metadata?.status;
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
62
80
|
await eventsManager.publishBus({
|
|
63
81
|
type: "flow.cancel",
|
|
64
82
|
runId,
|
|
65
83
|
flowName,
|
|
66
84
|
data: {
|
|
67
|
-
canceledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
85
|
+
canceledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
86
|
+
previousStatus
|
|
87
|
+
// Include for correct stats counter decrement
|
|
68
88
|
}
|
|
69
89
|
});
|
|
70
|
-
logger.info("Flow canceled", { flowName, runId });
|
|
90
|
+
logger.info("Flow canceled", { flowName, runId, previousStatus });
|
|
71
91
|
return { success: true, runId, flowName };
|
|
72
92
|
} catch (err) {
|
|
73
93
|
logger.error("Failed to cancel flow", { flowName, runId, error: err });
|
|
@@ -75,36 +95,55 @@ export function useFlow() {
|
|
|
75
95
|
}
|
|
76
96
|
},
|
|
77
97
|
/**
|
|
78
|
-
* Check if a flow is currently running
|
|
98
|
+
* Check if a flow is currently running (includes 'running' and 'awaiting' status)
|
|
99
|
+
* @param flowName - The name of the flow to check
|
|
100
|
+
* @param runId - Optional specific run ID to check (if provided, only checks that run)
|
|
101
|
+
* @param options - Optional configuration
|
|
102
|
+
* @param options.excludeRunIds - Exclude these run IDs from the check (useful when called from within a flow)
|
|
79
103
|
*/
|
|
80
|
-
async isRunning(flowName, runId) {
|
|
104
|
+
async isRunning(flowName, runId, options) {
|
|
81
105
|
try {
|
|
82
106
|
if (!store.index.read) {
|
|
83
107
|
return false;
|
|
84
108
|
}
|
|
85
109
|
const runIndexKey = StoreSubjects.flowRunIndex(flowName);
|
|
86
|
-
const
|
|
110
|
+
const activeStatuses = ["running", "awaiting"];
|
|
87
111
|
if (runId) {
|
|
88
|
-
const run =
|
|
89
|
-
return run?.metadata?.status
|
|
112
|
+
const run = await store.index.get(runIndexKey, runId);
|
|
113
|
+
return activeStatuses.includes(run?.metadata?.status);
|
|
90
114
|
}
|
|
91
|
-
|
|
115
|
+
const entries = await store.index.read(runIndexKey, {
|
|
116
|
+
limit: 1e3,
|
|
117
|
+
filter: { status: activeStatuses }
|
|
118
|
+
});
|
|
119
|
+
const excludeSet = new Set(options?.excludeRunIds || []);
|
|
120
|
+
const matchingRuns = entries.filter((e) => !excludeSet.has(e.id));
|
|
121
|
+
return matchingRuns.length > 0;
|
|
92
122
|
} catch (err) {
|
|
93
123
|
logger.error("Error checking flow status:", err);
|
|
94
124
|
return false;
|
|
95
125
|
}
|
|
96
126
|
},
|
|
97
127
|
/**
|
|
98
|
-
* Get all currently running flows
|
|
128
|
+
* Get all currently running flows (includes 'running' and 'awaiting' status)
|
|
129
|
+
* @param flowName - The name of the flow to check
|
|
130
|
+
* @param options - Optional configuration
|
|
131
|
+
* @param options.excludeRunIds - Exclude these run IDs from the results (useful when called from within a flow)
|
|
99
132
|
*/
|
|
100
|
-
async getRunningFlows(flowName) {
|
|
133
|
+
async getRunningFlows(flowName, options) {
|
|
101
134
|
try {
|
|
102
135
|
if (!store.index.read) {
|
|
103
136
|
return [];
|
|
104
137
|
}
|
|
105
138
|
const runIndexKey = StoreSubjects.flowRunIndex(flowName);
|
|
106
|
-
const
|
|
107
|
-
|
|
139
|
+
const activeStatuses = ["running", "awaiting"];
|
|
140
|
+
const entries = await store.index.read(runIndexKey, {
|
|
141
|
+
limit: 1e3,
|
|
142
|
+
filter: { status: activeStatuses }
|
|
143
|
+
});
|
|
144
|
+
const excludeSet = new Set(options?.excludeRunIds || []);
|
|
145
|
+
const filteredEntries = entries.filter((e) => !excludeSet.has(e.id));
|
|
146
|
+
return filteredEntries.map((e) => ({
|
|
108
147
|
id: e.id,
|
|
109
148
|
flowName,
|
|
110
149
|
status: e.metadata?.status,
|
|
@@ -5,18 +5,24 @@
|
|
|
5
5
|
export interface LifecycleHooks {
|
|
6
6
|
/**
|
|
7
7
|
* Called when await pattern is registered
|
|
8
|
-
* @param
|
|
8
|
+
* @param hookData - Type-safe data based on await type (webhookUrl, eventName, etc.)
|
|
9
9
|
* @param stepData - Current step data
|
|
10
|
-
* @param ctx - Worker context
|
|
10
|
+
* @param ctx - Worker context with awaitType and awaitConfig
|
|
11
11
|
*/
|
|
12
|
-
onAwaitRegister?: (
|
|
12
|
+
onAwaitRegister?: (hookData: any, stepData: any, ctx: any) => Promise<void>;
|
|
13
13
|
/**
|
|
14
14
|
* Called when await pattern is resolved
|
|
15
15
|
* @param resolvedData - Data from the trigger that resolved the await
|
|
16
16
|
* @param stepData - Current step data
|
|
17
|
-
* @param ctx - Worker context
|
|
17
|
+
* @param ctx - Worker context with awaitType
|
|
18
18
|
*/
|
|
19
19
|
onAwaitResolve?: (resolvedData: any, stepData: any, ctx: any) => Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Called when await pattern times out
|
|
22
|
+
* @param stepData - Current step data
|
|
23
|
+
* @param ctx - Worker context with awaitType and timeoutAction
|
|
24
|
+
*/
|
|
25
|
+
onAwaitTimeout?: (stepData: any, ctx: any) => Promise<void>;
|
|
20
26
|
}
|
|
21
27
|
export declare function useHookRegistry(): {
|
|
22
28
|
/**
|
|
@@ -110,6 +110,12 @@ export function useTrigger() {
|
|
|
110
110
|
`Emitting unregistered trigger '${name}'. Consider registering it first with registerTrigger().`
|
|
111
111
|
);
|
|
112
112
|
}
|
|
113
|
+
if (trigger && trigger.status !== "active") {
|
|
114
|
+
logger.info(
|
|
115
|
+
`Trigger '${name}' is ${trigger.status}, not emitting. Update status to 'active' to enable firing.`
|
|
116
|
+
);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
113
119
|
const threshold = opts?.payloadThreshold || trigger?.config?.payloadThreshold || 10 * 1024;
|
|
114
120
|
const eventData = await runtime.handleLargePayload(name, data, threshold);
|
|
115
121
|
logger.debug(`Emitting trigger: ${name}`, {
|
|
@@ -208,6 +214,7 @@ export function useTrigger() {
|
|
|
208
214
|
}
|
|
209
215
|
if (metadata.subscriptions) {
|
|
210
216
|
for (const [flowName, subData] of Object.entries(metadata.subscriptions)) {
|
|
217
|
+
if (!subData) continue;
|
|
211
218
|
const subscription = {
|
|
212
219
|
triggerName: entry.id,
|
|
213
220
|
flowName,
|
|
@@ -223,22 +230,6 @@ export function useTrigger() {
|
|
|
223
230
|
logger.info(
|
|
224
231
|
`Loaded ${activeCount} active triggers with ${totalSubscriptions} subscriptions from index`
|
|
225
232
|
);
|
|
226
|
-
} else {
|
|
227
|
-
logger.warn("Store does not support indexRead, falling back to doc-based loading");
|
|
228
|
-
if (store.list) {
|
|
229
|
-
const triggers = await store.list("triggers");
|
|
230
|
-
for (const { id, doc } of triggers) {
|
|
231
|
-
runtime.addTrigger(id, doc);
|
|
232
|
-
}
|
|
233
|
-
const subscriptions = await store.list("trigger-subscriptions");
|
|
234
|
-
for (const { doc } of subscriptions) {
|
|
235
|
-
const sub = doc;
|
|
236
|
-
runtime.addSubscription(sub.triggerName, sub.flowName, sub);
|
|
237
|
-
}
|
|
238
|
-
logger.info(
|
|
239
|
-
`Loaded ${triggers.length} triggers and ${subscriptions.length} subscriptions from doc store (legacy)`
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
233
|
}
|
|
243
234
|
runtime.setInitialized(true);
|
|
244
235
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Scheduler } from "./scheduler.js";
|
|
2
2
|
import { useRuntimeConfig } from "#imports";
|
|
3
|
-
|
|
3
|
+
const SCHEDULER_KEY = "__nvent_scheduler__";
|
|
4
|
+
let schedulerInstance = globalThis[SCHEDULER_KEY] || null;
|
|
4
5
|
export function createScheduler(store) {
|
|
5
6
|
const config = useRuntimeConfig();
|
|
6
7
|
const prefix = config.nvent.store?.prefix || "nvent";
|
|
@@ -24,6 +25,7 @@ export async function initializeScheduler(store) {
|
|
|
24
25
|
return schedulerInstance;
|
|
25
26
|
}
|
|
26
27
|
schedulerInstance = createScheduler(store);
|
|
28
|
+
globalThis[SCHEDULER_KEY] = schedulerInstance;
|
|
27
29
|
await schedulerInstance.start();
|
|
28
30
|
return schedulerInstance;
|
|
29
31
|
}
|
|
@@ -31,8 +33,10 @@ export async function shutdownScheduler() {
|
|
|
31
33
|
if (schedulerInstance) {
|
|
32
34
|
await schedulerInstance.stop();
|
|
33
35
|
schedulerInstance = null;
|
|
36
|
+
globalThis[SCHEDULER_KEY] = null;
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
export function resetScheduler() {
|
|
37
40
|
schedulerInstance = null;
|
|
41
|
+
globalThis[SCHEDULER_KEY] = null;
|
|
38
42
|
}
|