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,139 @@
|
|
|
1
|
+
import {
|
|
2
|
+
registerAwaitPattern,
|
|
3
|
+
resolveAwaitPattern,
|
|
4
|
+
registerWebhookAwait,
|
|
5
|
+
resolveWebhookAwait,
|
|
6
|
+
registerEventAwait,
|
|
7
|
+
resolveEventAwait,
|
|
8
|
+
registerScheduleAwait,
|
|
9
|
+
resolveScheduleAwait,
|
|
10
|
+
registerTimeAwait,
|
|
11
|
+
resolveTimeAwait
|
|
12
|
+
} from "./awaitPatterns/index.js";
|
|
13
|
+
import { useStoreAdapter, useStreamTopics, useNventLogger } from "#imports";
|
|
14
|
+
export function useAwait() {
|
|
15
|
+
const logger = useNventLogger("await");
|
|
16
|
+
const store = useStoreAdapter();
|
|
17
|
+
const { StoreSubjects } = useStreamTopics();
|
|
18
|
+
return {
|
|
19
|
+
/**
|
|
20
|
+
* Register an await pattern based on config type
|
|
21
|
+
* Automatically routes to appropriate implementation
|
|
22
|
+
*/
|
|
23
|
+
register: registerAwaitPattern,
|
|
24
|
+
/**
|
|
25
|
+
* Resolve an await pattern by type
|
|
26
|
+
*/
|
|
27
|
+
resolve: resolveAwaitPattern,
|
|
28
|
+
/**
|
|
29
|
+
* Direct access to specific await pattern implementations
|
|
30
|
+
*/
|
|
31
|
+
webhook: {
|
|
32
|
+
register: registerWebhookAwait,
|
|
33
|
+
resolve: resolveWebhookAwait
|
|
34
|
+
},
|
|
35
|
+
event: {
|
|
36
|
+
register: registerEventAwait,
|
|
37
|
+
resolve: resolveEventAwait
|
|
38
|
+
},
|
|
39
|
+
schedule: {
|
|
40
|
+
register: registerScheduleAwait,
|
|
41
|
+
resolve: resolveScheduleAwait
|
|
42
|
+
},
|
|
43
|
+
time: {
|
|
44
|
+
register: registerTimeAwait,
|
|
45
|
+
resolve: resolveTimeAwait
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* Query methods for await state
|
|
49
|
+
*/
|
|
50
|
+
/**
|
|
51
|
+
* Get await state for a specific flow run and step
|
|
52
|
+
*/
|
|
53
|
+
async getAwaitState(flowName, runId, stepName) {
|
|
54
|
+
const indexKey = StoreSubjects.flowRunIndex(flowName);
|
|
55
|
+
if (!store.index.get) {
|
|
56
|
+
logger.warn("Store does not support indexGet");
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const entry = await store.index.get(indexKey, runId);
|
|
60
|
+
if (!entry?.metadata) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const awaitingSteps = entry.metadata.awaitingSteps || {};
|
|
64
|
+
if (stepName) {
|
|
65
|
+
return awaitingSteps[stepName] || null;
|
|
66
|
+
}
|
|
67
|
+
return awaitingSteps;
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Check if a step is currently awaiting
|
|
71
|
+
*/
|
|
72
|
+
async isAwaiting(flowName, runId, stepName) {
|
|
73
|
+
const awaitState = await this.getAwaitState(flowName, runId, stepName);
|
|
74
|
+
return awaitState?.status === "awaiting";
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* Get all active awaits across all flows
|
|
78
|
+
*/
|
|
79
|
+
async getAllActiveAwaits(flowName) {
|
|
80
|
+
const activeAwaits = [];
|
|
81
|
+
if (!store.indexScan) {
|
|
82
|
+
logger.warn("Store does not support indexScan");
|
|
83
|
+
return activeAwaits;
|
|
84
|
+
}
|
|
85
|
+
if (flowName) {
|
|
86
|
+
const indexKey = StoreSubjects.flowRunIndex(flowName);
|
|
87
|
+
const entries = await store.index.read(indexKey, { limit: 1e3 });
|
|
88
|
+
for (const entry of entries) {
|
|
89
|
+
const awaitingSteps = entry.metadata?.awaitingSteps || {};
|
|
90
|
+
for (const [stepName, awaitState] of Object.entries(awaitingSteps)) {
|
|
91
|
+
if (awaitState.status === "awaiting") {
|
|
92
|
+
activeAwaits.push({
|
|
93
|
+
flowName,
|
|
94
|
+
runId: entry.id,
|
|
95
|
+
stepName,
|
|
96
|
+
awaitType: awaitState.awaitType,
|
|
97
|
+
position: awaitState.position,
|
|
98
|
+
registeredAt: awaitState.registeredAt
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
logger.warn("Scanning all flows not yet implemented - provide flowName parameter");
|
|
105
|
+
}
|
|
106
|
+
return activeAwaits;
|
|
107
|
+
},
|
|
108
|
+
/**
|
|
109
|
+
* Get await history for a specific flow run from stream
|
|
110
|
+
*/
|
|
111
|
+
async getAwaitHistory(runId, opts) {
|
|
112
|
+
const streamName = StoreSubjects.flowRun(runId);
|
|
113
|
+
const events = await store.stream.read(streamName, {
|
|
114
|
+
limit: opts?.limit || 100,
|
|
115
|
+
types: ["await.registered", "await.resolved", "await.timeout"],
|
|
116
|
+
order: "desc"
|
|
117
|
+
});
|
|
118
|
+
if (opts?.stepName) {
|
|
119
|
+
return events.filter((e) => e.stepName === opts.stepName);
|
|
120
|
+
}
|
|
121
|
+
return events;
|
|
122
|
+
},
|
|
123
|
+
/**
|
|
124
|
+
* Cancel/timeout an active await
|
|
125
|
+
*/
|
|
126
|
+
async timeoutAwait(flowName, runId, stepName, reason = "Manual timeout") {
|
|
127
|
+
const indexKey = StoreSubjects.flowRunIndex(flowName);
|
|
128
|
+
if (store.index.updateWithRetry) {
|
|
129
|
+
await store.index.updateWithRetry(indexKey, runId, {
|
|
130
|
+
[`awaitingSteps.${stepName}.status`]: "timeout",
|
|
131
|
+
[`awaitingSteps.${stepName}.timeoutReason`]: reason,
|
|
132
|
+
[`awaitingSteps.${stepName}.timeoutAt`]: (/* @__PURE__ */ new Date()).toISOString()
|
|
133
|
+
});
|
|
134
|
+
logger.info("Await timed out", { flowName, runId, stepName, reason });
|
|
135
|
+
}
|
|
136
|
+
logger.warn("await.timeout event publishing not yet implemented in useAwait");
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getEventBus } from '
|
|
2
|
-
import type { EventRecord } from '
|
|
1
|
+
import { getEventBus } from '../../events/eventBus.js';
|
|
2
|
+
import type { EventRecord } from '../../adapters/interfaces/store.js';
|
|
3
3
|
export interface EventManager {
|
|
4
4
|
/**
|
|
5
5
|
* Publish an event directly to the in-proc bus.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export interface FlowStats {
|
|
2
|
+
name: string;
|
|
3
|
+
displayName?: string;
|
|
4
|
+
registeredAt: string;
|
|
5
|
+
lastRunAt?: string;
|
|
6
|
+
lastCompletedAt?: string;
|
|
7
|
+
stats: {
|
|
8
|
+
total: number;
|
|
9
|
+
success: number;
|
|
10
|
+
failure: number;
|
|
11
|
+
cancel: number;
|
|
12
|
+
running: number;
|
|
13
|
+
awaiting: number;
|
|
14
|
+
};
|
|
15
|
+
version?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Flow composable for managing flows and accessing flow statistics
|
|
19
|
+
* Provides methods for starting, canceling, and querying flows
|
|
20
|
+
*/
|
|
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
|
+
};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { $useFunctionRegistry, useQueueAdapter, useEventManager, useStoreAdapter, useNventLogger, useStreamTopics } from "#imports";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
export function useFlow() {
|
|
4
|
+
const registry = $useFunctionRegistry();
|
|
5
|
+
const queueAdapter = useQueueAdapter();
|
|
6
|
+
const eventsManager = useEventManager();
|
|
7
|
+
const store = useStoreAdapter();
|
|
8
|
+
const logger = useNventLogger("use-flow");
|
|
9
|
+
const { StoreSubjects } = useStreamTopics();
|
|
10
|
+
return {
|
|
11
|
+
/**
|
|
12
|
+
* Start a flow with the given payload
|
|
13
|
+
*/
|
|
14
|
+
async startFlow(flowName, payload = {}) {
|
|
15
|
+
const flow = registry?.flows?.[flowName];
|
|
16
|
+
if (!flow || !flow.entry) throw new Error("Flow not found");
|
|
17
|
+
const queueName = typeof flow.entry.queue === "string" ? flow.entry.queue : flow.entry.queue?.name || flow.entry.queue;
|
|
18
|
+
const entryWorker = registry?.workers?.find(
|
|
19
|
+
(w) => w?.flow?.step === flow.entry.step && w?.queue?.name === queueName
|
|
20
|
+
);
|
|
21
|
+
const opts = entryWorker?.queue?.defaultJobOptions || {};
|
|
22
|
+
const flowId = randomUUID();
|
|
23
|
+
const id = await queueAdapter.enqueue(queueName, { name: flow.entry.step, data: { ...payload, flowId, flowName }, opts });
|
|
24
|
+
try {
|
|
25
|
+
await eventsManager.publishBus({ type: "flow.start", runId: flowId, flowName, data: { input: payload } });
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
return { id, queue: queueName, step: flow.entry.step, flowId };
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* Emit a trigger event for flow coordination
|
|
32
|
+
*/
|
|
33
|
+
async emit(trigger, payload = {}) {
|
|
34
|
+
const flowId = payload?.flowId;
|
|
35
|
+
const flowName = payload?.flowName || "unknown";
|
|
36
|
+
const stepName = payload?.stepName;
|
|
37
|
+
if (!flowId) {
|
|
38
|
+
logger.warn("emit called without flowId, trigger may not work", { trigger });
|
|
39
|
+
}
|
|
40
|
+
const { flowId: _, flowName: __, stepName: ___, ...actualPayload } = payload;
|
|
41
|
+
try {
|
|
42
|
+
await eventsManager.publishBus({
|
|
43
|
+
type: "emit",
|
|
44
|
+
runId: flowId || "unknown",
|
|
45
|
+
flowName,
|
|
46
|
+
stepName,
|
|
47
|
+
data: {
|
|
48
|
+
name: trigger,
|
|
49
|
+
payload: actualPayload
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
} catch (err) {
|
|
53
|
+
logger.error("Failed to emit trigger event", { trigger, error: err });
|
|
54
|
+
}
|
|
55
|
+
return [];
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* Cancel a running flow
|
|
59
|
+
*/
|
|
60
|
+
async cancelFlow(flowName, runId) {
|
|
61
|
+
try {
|
|
62
|
+
await eventsManager.publishBus({
|
|
63
|
+
type: "flow.cancel",
|
|
64
|
+
runId,
|
|
65
|
+
flowName,
|
|
66
|
+
data: {
|
|
67
|
+
canceledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
logger.info("Flow canceled", { flowName, runId });
|
|
71
|
+
return { success: true, runId, flowName };
|
|
72
|
+
} catch (err) {
|
|
73
|
+
logger.error("Failed to cancel flow", { flowName, runId, error: err });
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* Check if a flow is currently running
|
|
79
|
+
*/
|
|
80
|
+
async isRunning(flowName, runId) {
|
|
81
|
+
try {
|
|
82
|
+
if (!store.index.read) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
const runIndexKey = StoreSubjects.flowRunIndex(flowName);
|
|
86
|
+
const entries = await store.index.read(runIndexKey, { limit: 1e3 });
|
|
87
|
+
if (runId) {
|
|
88
|
+
const run = entries.find((e) => e.id === runId);
|
|
89
|
+
return run?.metadata?.status === "running";
|
|
90
|
+
}
|
|
91
|
+
return entries.some((e) => e.metadata?.status === "running");
|
|
92
|
+
} catch (err) {
|
|
93
|
+
logger.error("Error checking flow status:", err);
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
/**
|
|
98
|
+
* Get all currently running flows
|
|
99
|
+
*/
|
|
100
|
+
async getRunningFlows(flowName) {
|
|
101
|
+
try {
|
|
102
|
+
if (!store.index.read) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
const runIndexKey = StoreSubjects.flowRunIndex(flowName);
|
|
106
|
+
const entries = await store.index.read(runIndexKey, { limit: 1e3 });
|
|
107
|
+
return entries.filter((e) => e.metadata?.status === "running").map((e) => ({
|
|
108
|
+
id: e.id,
|
|
109
|
+
flowName,
|
|
110
|
+
status: e.metadata?.status,
|
|
111
|
+
startedAt: e.metadata?.startedAt,
|
|
112
|
+
stepCount: e.metadata?.stepCount || 0,
|
|
113
|
+
completedSteps: e.metadata?.completedSteps || 0
|
|
114
|
+
}));
|
|
115
|
+
} catch (err) {
|
|
116
|
+
logger.error("Error getting running flows:", err);
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
/**
|
|
121
|
+
* Get flow statistics by name
|
|
122
|
+
*/
|
|
123
|
+
async getFlowStats(flowName) {
|
|
124
|
+
try {
|
|
125
|
+
const indexKey = StoreSubjects.flowIndex();
|
|
126
|
+
if (!store.index.get) {
|
|
127
|
+
logger.warn("Store adapter does not support indexGet");
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const entry = await store.index.get(indexKey, flowName);
|
|
131
|
+
if (!entry) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
const metadata = entry.metadata;
|
|
135
|
+
return {
|
|
136
|
+
name: metadata.name || flowName,
|
|
137
|
+
displayName: metadata.displayName,
|
|
138
|
+
registeredAt: metadata.registeredAt,
|
|
139
|
+
lastRunAt: metadata.lastRunAt,
|
|
140
|
+
lastCompletedAt: metadata.lastCompletedAt,
|
|
141
|
+
stats: {
|
|
142
|
+
total: metadata.stats?.total || 0,
|
|
143
|
+
success: metadata.stats?.success || 0,
|
|
144
|
+
failure: metadata.stats?.failure || 0,
|
|
145
|
+
cancel: metadata.stats?.cancel || 0,
|
|
146
|
+
running: metadata.stats?.running || 0,
|
|
147
|
+
awaiting: metadata.stats?.awaiting || 0
|
|
148
|
+
},
|
|
149
|
+
version: metadata.version
|
|
150
|
+
};
|
|
151
|
+
} catch (err) {
|
|
152
|
+
logger.error("Error getting flow stats", {
|
|
153
|
+
flowName,
|
|
154
|
+
error: err?.message
|
|
155
|
+
});
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
/**
|
|
160
|
+
* Get all flows with their statistics
|
|
161
|
+
*/
|
|
162
|
+
async getAllFlowStats(options) {
|
|
163
|
+
try {
|
|
164
|
+
const indexKey = StoreSubjects.flowIndex();
|
|
165
|
+
if (!store.index.read) {
|
|
166
|
+
logger.warn("Store adapter does not support indexRead");
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
const entries = await store.index.read(indexKey, {
|
|
170
|
+
limit: options?.limit || 1e3,
|
|
171
|
+
offset: options?.offset || 0
|
|
172
|
+
});
|
|
173
|
+
const flows = entries.map((entry) => {
|
|
174
|
+
const metadata = entry.metadata;
|
|
175
|
+
return {
|
|
176
|
+
name: metadata.name || entry.id,
|
|
177
|
+
displayName: metadata.displayName,
|
|
178
|
+
registeredAt: metadata.registeredAt,
|
|
179
|
+
lastRunAt: metadata.lastRunAt,
|
|
180
|
+
lastCompletedAt: metadata.lastCompletedAt,
|
|
181
|
+
stats: {
|
|
182
|
+
total: metadata.stats?.total || 0,
|
|
183
|
+
success: metadata.stats?.success || 0,
|
|
184
|
+
failure: metadata.stats?.failure || 0,
|
|
185
|
+
cancel: metadata.stats?.cancel || 0,
|
|
186
|
+
running: metadata.stats?.running || 0,
|
|
187
|
+
awaiting: metadata.stats?.awaiting || 0
|
|
188
|
+
},
|
|
189
|
+
version: metadata.version
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
if (options?.sortBy) {
|
|
193
|
+
flows.sort((a, b) => {
|
|
194
|
+
let aVal;
|
|
195
|
+
let bVal;
|
|
196
|
+
if (options.sortBy === "name") {
|
|
197
|
+
aVal = a.name;
|
|
198
|
+
bVal = b.name;
|
|
199
|
+
} else if (options.sortBy === "registeredAt") {
|
|
200
|
+
aVal = new Date(a.registeredAt).getTime();
|
|
201
|
+
bVal = new Date(b.registeredAt).getTime();
|
|
202
|
+
} else if (options.sortBy === "lastRunAt") {
|
|
203
|
+
aVal = a.lastRunAt ? new Date(a.lastRunAt).getTime() : 0;
|
|
204
|
+
bVal = b.lastRunAt ? new Date(b.lastRunAt).getTime() : 0;
|
|
205
|
+
}
|
|
206
|
+
const order = options.order === "desc" ? -1 : 1;
|
|
207
|
+
return (aVal > bVal ? 1 : aVal < bVal ? -1 : 0) * order;
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return flows;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
logger.error("Error getting all flow stats", {
|
|
213
|
+
error: err?.message
|
|
214
|
+
});
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
/**
|
|
219
|
+
* Check if a flow has statistics in the index
|
|
220
|
+
*/
|
|
221
|
+
async hasFlowStats(flowName) {
|
|
222
|
+
const stats = await this.getFlowStats(flowName);
|
|
223
|
+
return stats !== null;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle hooks registry for await patterns
|
|
3
|
+
* v0.5 - Await Integration
|
|
4
|
+
*/
|
|
5
|
+
export interface LifecycleHooks {
|
|
6
|
+
/**
|
|
7
|
+
* Called when await pattern is registered
|
|
8
|
+
* @param webhookUrl - Generated webhook URL (for webhook awaits) or event/schedule info
|
|
9
|
+
* @param stepData - Current step data
|
|
10
|
+
* @param ctx - Worker context
|
|
11
|
+
*/
|
|
12
|
+
onAwaitRegister?: (webhookUrl: string, stepData: any, ctx: any) => Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Called when await pattern is resolved
|
|
15
|
+
* @param resolvedData - Data from the trigger that resolved the await
|
|
16
|
+
* @param stepData - Current step data
|
|
17
|
+
* @param ctx - Worker context
|
|
18
|
+
*/
|
|
19
|
+
onAwaitResolve?: (resolvedData: any, stepData: any, ctx: any) => Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export declare function useHookRegistry(): {
|
|
22
|
+
/**
|
|
23
|
+
* Register lifecycle hooks for a specific flow step
|
|
24
|
+
*/
|
|
25
|
+
register(flowName: string, stepName: string, hooks: LifecycleHooks): void;
|
|
26
|
+
/**
|
|
27
|
+
* Load lifecycle hooks for a specific flow step
|
|
28
|
+
*/
|
|
29
|
+
load(flowName: string, stepName: string): LifecycleHooks | null;
|
|
30
|
+
/**
|
|
31
|
+
* Clear all registered hooks (useful for testing)
|
|
32
|
+
*/
|
|
33
|
+
clear(): void;
|
|
34
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const hookRegistry = /* @__PURE__ */ new Map();
|
|
2
|
+
export function useHookRegistry() {
|
|
3
|
+
return {
|
|
4
|
+
/**
|
|
5
|
+
* Register lifecycle hooks for a specific flow step
|
|
6
|
+
*/
|
|
7
|
+
register(flowName, stepName, hooks) {
|
|
8
|
+
const key = `${flowName}:${stepName}`;
|
|
9
|
+
hookRegistry.set(key, hooks);
|
|
10
|
+
},
|
|
11
|
+
/**
|
|
12
|
+
* Load lifecycle hooks for a specific flow step
|
|
13
|
+
*/
|
|
14
|
+
load(flowName, stepName) {
|
|
15
|
+
const key = `${flowName}:${stepName}`;
|
|
16
|
+
return hookRegistry.get(key) || null;
|
|
17
|
+
},
|
|
18
|
+
/**
|
|
19
|
+
* Clear all registered hooks (useful for testing)
|
|
20
|
+
*/
|
|
21
|
+
clear() {
|
|
22
|
+
hookRegistry.clear();
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RunContext } from '../worker/node/runner.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a minimal RunContext for use in lifecycle hooks and event handlers
|
|
4
|
+
* This is a lightweight version without the full worker context
|
|
5
|
+
*/
|
|
6
|
+
export declare function useRunContext(partial?: Partial<RunContext>): RunContext;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { useEventManager, useFlow, useRuntimeConfig, useStateAdapter } from "#imports";
|
|
2
|
+
const defaultState = {
|
|
3
|
+
async get() {
|
|
4
|
+
return null;
|
|
5
|
+
},
|
|
6
|
+
async set() {
|
|
7
|
+
},
|
|
8
|
+
async delete() {
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function scopeKey(baseKey, flowId) {
|
|
12
|
+
if (!flowId) return baseKey;
|
|
13
|
+
return `flow:${flowId}:${baseKey}`;
|
|
14
|
+
}
|
|
15
|
+
export function useRunContext(partial) {
|
|
16
|
+
const state = partial?.state || (() => {
|
|
17
|
+
try {
|
|
18
|
+
const stateAdapter = useStateAdapter();
|
|
19
|
+
const rc = useRuntimeConfig();
|
|
20
|
+
const cleanupCfg = rc?.nvent?.state?.cleanup || { strategy: "never" };
|
|
21
|
+
return {
|
|
22
|
+
async get(key) {
|
|
23
|
+
return stateAdapter.get(scopeKey(key, partial?.flowId));
|
|
24
|
+
},
|
|
25
|
+
async set(key, value, opts) {
|
|
26
|
+
const ttl = opts?.ttl ?? (cleanupCfg?.strategy === "ttl" ? cleanupCfg?.ttlMs : void 0);
|
|
27
|
+
return stateAdapter.set(scopeKey(key, partial?.flowId), value, ttl ? { ttl } : void 0);
|
|
28
|
+
},
|
|
29
|
+
async delete(key) {
|
|
30
|
+
return stateAdapter.delete(scopeKey(key, partial?.flowId));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
} catch {
|
|
34
|
+
return defaultState;
|
|
35
|
+
}
|
|
36
|
+
})();
|
|
37
|
+
const logger = partial?.logger || (() => {
|
|
38
|
+
const eventManager = useEventManager();
|
|
39
|
+
return {
|
|
40
|
+
log: (level, msg, meta) => {
|
|
41
|
+
const runId = partial?.flowId || "unknown";
|
|
42
|
+
const flowName = meta?.flowName || partial?.flowName || "unknown";
|
|
43
|
+
void eventManager.publishBus({
|
|
44
|
+
type: "log",
|
|
45
|
+
runId,
|
|
46
|
+
flowName,
|
|
47
|
+
stepName: meta?.stepName || partial?.stepName,
|
|
48
|
+
stepId: meta?.stepId,
|
|
49
|
+
attempt: meta?.attempt,
|
|
50
|
+
data: { level, message: msg, ...meta }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
})();
|
|
55
|
+
const baseFlowEngine = useFlow();
|
|
56
|
+
const flow = {
|
|
57
|
+
...baseFlowEngine,
|
|
58
|
+
emit: async (trigger, payload = {}) => {
|
|
59
|
+
const enrichedPayload = {
|
|
60
|
+
...payload,
|
|
61
|
+
flowId: payload.flowId || partial?.flowId,
|
|
62
|
+
flowName: payload.flowName || partial?.flowName,
|
|
63
|
+
stepName: payload.stepName || partial?.stepName
|
|
64
|
+
};
|
|
65
|
+
return baseFlowEngine.emit(trigger, enrichedPayload);
|
|
66
|
+
},
|
|
67
|
+
cancel: async () => {
|
|
68
|
+
if (!partial?.flowName || !partial?.flowId) {
|
|
69
|
+
throw new Error("Cannot cancel flow: flowName or flowId not available in context");
|
|
70
|
+
}
|
|
71
|
+
return baseFlowEngine.cancelFlow(partial.flowName, partial.flowId);
|
|
72
|
+
},
|
|
73
|
+
isRunning: async (flowName, runId) => {
|
|
74
|
+
const targetFlowName = flowName || partial?.flowName;
|
|
75
|
+
if (!targetFlowName) {
|
|
76
|
+
throw new Error("flowName is required to check if flow is running");
|
|
77
|
+
}
|
|
78
|
+
return baseFlowEngine.isRunning(targetFlowName, runId);
|
|
79
|
+
},
|
|
80
|
+
getRunningFlows: async (flowName) => {
|
|
81
|
+
const targetFlowName = flowName || partial?.flowName;
|
|
82
|
+
if (!targetFlowName) {
|
|
83
|
+
throw new Error("flowName is required to get running flows");
|
|
84
|
+
}
|
|
85
|
+
return baseFlowEngine.getRunningFlows(targetFlowName);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
jobId: partial?.jobId,
|
|
90
|
+
queue: partial?.queue,
|
|
91
|
+
flowId: partial?.flowId,
|
|
92
|
+
flowName: partial?.flowName,
|
|
93
|
+
stepName: partial?.stepName,
|
|
94
|
+
stepId: partial?.stepId,
|
|
95
|
+
attempt: partial?.attempt,
|
|
96
|
+
logger,
|
|
97
|
+
state,
|
|
98
|
+
flow,
|
|
99
|
+
trigger: partial?.trigger,
|
|
100
|
+
awaitConfig: partial?.awaitConfig
|
|
101
|
+
};
|
|
102
|
+
}
|