nvent 0.4.5 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/module.d.mts +1 -1
  2. package/dist/module.mjs +433 -175
  3. package/dist/runtime/adapters/base/index.d.ts +6 -0
  4. package/dist/runtime/adapters/base/index.js +1 -0
  5. package/dist/runtime/adapters/base/store-validator.d.ts +48 -0
  6. package/dist/runtime/adapters/base/store-validator.js +147 -0
  7. package/dist/runtime/adapters/builtin/file-queue.d.ts +15 -1
  8. package/dist/runtime/adapters/builtin/file-queue.js +70 -6
  9. package/dist/runtime/adapters/builtin/file-store.d.ts +4 -18
  10. package/dist/runtime/adapters/builtin/file-store.js +90 -109
  11. package/dist/runtime/adapters/builtin/memory-queue.js +4 -0
  12. package/dist/runtime/adapters/builtin/memory-store.d.ts +42 -31
  13. package/dist/runtime/adapters/builtin/memory-store.js +253 -183
  14. package/dist/runtime/adapters/factory.d.ts +2 -2
  15. package/dist/runtime/adapters/factory.js +54 -20
  16. package/dist/runtime/adapters/interfaces/store.d.ts +177 -113
  17. package/dist/runtime/config/index.d.ts +2 -2
  18. package/dist/runtime/config/index.js +14 -6
  19. package/dist/runtime/config/types.d.ts +32 -2
  20. package/dist/runtime/events/eventBus.d.ts +1 -1
  21. package/dist/runtime/events/types.d.ts +31 -2
  22. package/dist/runtime/events/utils/scheduleTrigger.d.ts +8 -0
  23. package/dist/runtime/events/utils/scheduleTrigger.js +69 -0
  24. package/dist/runtime/events/utils/stallDetector.d.ts +44 -3
  25. package/dist/runtime/events/utils/stallDetector.js +288 -89
  26. package/dist/runtime/events/utils/triggerRuntime.d.ts +58 -0
  27. package/dist/runtime/events/utils/triggerRuntime.js +212 -0
  28. package/dist/runtime/events/wiring/flowWiring.d.ts +11 -5
  29. package/dist/runtime/events/wiring/flowWiring.js +620 -92
  30. package/dist/runtime/events/wiring/registry.d.ts +2 -2
  31. package/dist/runtime/events/wiring/registry.js +8 -6
  32. package/dist/runtime/events/wiring/streamWiring.d.ts +15 -11
  33. package/dist/runtime/events/wiring/streamWiring.js +88 -11
  34. package/dist/runtime/events/wiring/triggerWiring.d.ts +21 -0
  35. package/dist/runtime/events/wiring/triggerWiring.js +412 -0
  36. package/dist/runtime/{server → nitro}/plugins/00.adapters.js +8 -4
  37. package/dist/runtime/{server → nitro}/plugins/02.workers.js +21 -3
  38. package/dist/runtime/nitro/plugins/03.triggers.d.ts +12 -0
  39. package/dist/runtime/nitro/plugins/03.triggers.js +55 -0
  40. package/dist/runtime/nitro/routes/webhook.await.d.ts +23 -0
  41. package/dist/runtime/nitro/routes/webhook.await.js +90 -0
  42. package/dist/runtime/nitro/routes/webhook.trigger.d.ts +69 -0
  43. package/dist/runtime/nitro/routes/webhook.trigger.js +64 -0
  44. package/dist/runtime/{utils → nitro/utils}/adapters.d.ts +6 -6
  45. package/dist/runtime/nitro/utils/awaitPatterns/event.d.ts +15 -0
  46. package/dist/runtime/nitro/utils/awaitPatterns/event.js +120 -0
  47. package/dist/runtime/nitro/utils/awaitPatterns/index.d.ts +28 -0
  48. package/dist/runtime/nitro/utils/awaitPatterns/index.js +55 -0
  49. package/dist/runtime/nitro/utils/awaitPatterns/schedule.d.ts +16 -0
  50. package/dist/runtime/nitro/utils/awaitPatterns/schedule.js +78 -0
  51. package/dist/runtime/nitro/utils/awaitPatterns/time.d.ts +15 -0
  52. package/dist/runtime/nitro/utils/awaitPatterns/time.js +67 -0
  53. package/dist/runtime/nitro/utils/awaitPatterns/webhook.d.ts +15 -0
  54. package/dist/runtime/nitro/utils/awaitPatterns/webhook.js +120 -0
  55. package/dist/runtime/{utils → nitro/utils}/defineFunction.d.ts +2 -2
  56. package/dist/runtime/{utils → nitro/utils}/defineFunction.js +3 -3
  57. package/dist/runtime/{utils → nitro/utils}/defineFunctionConfig.d.ts +156 -0
  58. package/dist/runtime/{utils → nitro/utils}/defineFunctionConfig.js +1 -0
  59. package/dist/runtime/nitro/utils/defineHooks.d.ts +41 -0
  60. package/dist/runtime/nitro/utils/defineHooks.js +6 -0
  61. package/dist/runtime/{utils → nitro/utils}/registerAdapter.d.ts +3 -3
  62. package/dist/runtime/{utils → nitro/utils}/registerAdapter.js +1 -1
  63. package/dist/runtime/nitro/utils/useAwait.d.ts +71 -0
  64. package/dist/runtime/nitro/utils/useAwait.js +139 -0
  65. package/dist/runtime/{utils → nitro/utils}/useEventManager.d.ts +2 -2
  66. package/dist/runtime/{utils → nitro/utils}/useEventManager.js +1 -1
  67. package/dist/runtime/nitro/utils/useFlow.d.ts +68 -0
  68. package/dist/runtime/nitro/utils/useFlow.js +226 -0
  69. package/dist/runtime/nitro/utils/useHookRegistry.d.ts +34 -0
  70. package/dist/runtime/nitro/utils/useHookRegistry.js +25 -0
  71. package/dist/runtime/nitro/utils/useRunContext.d.ts +6 -0
  72. package/dist/runtime/nitro/utils/useRunContext.js +102 -0
  73. package/dist/runtime/nitro/utils/useStreamTopics.d.ts +83 -0
  74. package/dist/runtime/nitro/utils/useStreamTopics.js +94 -0
  75. package/dist/runtime/nitro/utils/useTrigger.d.ts +150 -0
  76. package/dist/runtime/nitro/utils/useTrigger.js +320 -0
  77. package/dist/runtime/scheduler/index.d.ts +33 -0
  78. package/dist/runtime/scheduler/index.js +38 -0
  79. package/dist/runtime/scheduler/scheduler.d.ts +113 -0
  80. package/dist/runtime/scheduler/scheduler.js +623 -0
  81. package/dist/runtime/scheduler/types.d.ts +116 -0
  82. package/dist/runtime/scheduler/types.js +0 -0
  83. package/dist/runtime/worker/node/runner.d.ts +12 -2
  84. package/dist/runtime/worker/node/runner.js +141 -37
  85. package/package.json +6 -6
  86. package/dist/runtime/server/api/_flows/[name]/clear-history.delete.d.ts +0 -10
  87. package/dist/runtime/server/api/_flows/[name]/clear-history.delete.js +0 -55
  88. package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.d.ts +0 -2
  89. package/dist/runtime/server/api/_flows/[name]/runs/[runId]/cancel.post.js +0 -21
  90. package/dist/runtime/server/api/_flows/[name]/runs.get.d.ts +0 -17
  91. package/dist/runtime/server/api/_flows/[name]/runs.get.js +0 -64
  92. package/dist/runtime/server/api/_flows/[name]/schedule.post.d.ts +0 -2
  93. package/dist/runtime/server/api/_flows/[name]/schedule.post.js +0 -66
  94. package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.d.ts +0 -2
  95. package/dist/runtime/server/api/_flows/[name]/schedules/[id].delete.js +0 -47
  96. package/dist/runtime/server/api/_flows/[name]/schedules.get.d.ts +0 -2
  97. package/dist/runtime/server/api/_flows/[name]/schedules.get.js +0 -50
  98. package/dist/runtime/server/api/_flows/[name]/start.post.d.ts +0 -2
  99. package/dist/runtime/server/api/_flows/[name]/start.post.js +0 -9
  100. package/dist/runtime/server/api/_flows/index.get.d.ts +0 -6
  101. package/dist/runtime/server/api/_flows/index.get.js +0 -5
  102. package/dist/runtime/server/api/_flows/ws.d.ts +0 -60
  103. package/dist/runtime/server/api/_flows/ws.js +0 -209
  104. package/dist/runtime/server/api/_queues/[name]/job/[id].get.d.ts +0 -2
  105. package/dist/runtime/server/api/_queues/[name]/job/[id].get.js +0 -14
  106. package/dist/runtime/server/api/_queues/[name]/job/index.get.d.ts +0 -2
  107. package/dist/runtime/server/api/_queues/[name]/job/index.get.js +0 -27
  108. package/dist/runtime/server/api/_queues/index.get.d.ts +0 -2
  109. package/dist/runtime/server/api/_queues/index.get.js +0 -106
  110. package/dist/runtime/server/api/_queues/ws.d.ts +0 -48
  111. package/dist/runtime/server/api/_queues/ws.js +0 -215
  112. package/dist/runtime/utils/useFlowEngine.d.ts +0 -19
  113. package/dist/runtime/utils/useFlowEngine.js +0 -108
  114. package/dist/runtime/utils/useStreamTopics.d.ts +0 -72
  115. package/dist/runtime/utils/useStreamTopics.js +0 -47
  116. /package/dist/runtime/{server → nitro}/plugins/00.adapters.d.ts +0 -0
  117. /package/dist/runtime/{server → nitro}/plugins/01.ws-lifecycle.d.ts +0 -0
  118. /package/dist/runtime/{server → nitro}/plugins/01.ws-lifecycle.js +0 -0
  119. /package/dist/runtime/{server → nitro}/plugins/02.workers.d.ts +0 -0
  120. /package/dist/runtime/{utils → nitro/utils}/adapters.js +0 -0
  121. /package/dist/runtime/{utils → nitro/utils}/useNventLogger.d.ts +0 -0
  122. /package/dist/runtime/{utils → nitro/utils}/useNventLogger.js +0 -0
  123. /package/dist/runtime/{utils → nitro/utils}/wsPeerManager.d.ts +0 -0
  124. /package/dist/runtime/{utils → nitro/utils}/wsPeerManager.js +0 -0
@@ -1,6 +1,6 @@
1
1
  export interface Wiring {
2
- start(): void;
3
- stop(): void;
2
+ start(): void | Promise<void>;
3
+ stop(): void | Promise<void>;
4
4
  }
5
5
  export interface WiringRegistryOptions {
6
6
  /**
@@ -1,6 +1,7 @@
1
1
  import { createFlowWiring } from "./flowWiring.js";
2
2
  import { createStreamWiring } from "./streamWiring.js";
3
3
  import { createStateWiring } from "./stateWiring.js";
4
+ import { createTriggerWiring } from "./triggerWiring.js";
4
5
  export function createWiringRegistry(opts) {
5
6
  const wirings = [
6
7
  // 1. Flow orchestration (persistence, completion tracking, step triggering)
@@ -10,20 +11,21 @@ export function createWiringRegistry(opts) {
10
11
  enabled: true
11
12
  }),
12
13
  // 3. State wiring (automatic state cleanup)
13
- createStateWiring(opts?.stateWiring)
14
- // Future wirings: triggers, webhooks, etc.
14
+ createStateWiring(opts?.stateWiring),
15
+ // 4. Trigger wiring (v0.5: trigger.fired, await.registered, await.resolved)
16
+ createTriggerWiring()
15
17
  ];
16
18
  let started = false;
17
19
  return {
18
- start() {
20
+ async start() {
19
21
  if (started) return;
20
22
  started = true;
21
- for (const w of wirings) w.start();
23
+ for (const w of wirings) await w.start();
22
24
  },
23
- stop() {
25
+ async stop() {
24
26
  for (const w of wirings) {
25
27
  try {
26
- w.stop();
28
+ await w.stop();
27
29
  } catch {
28
30
  }
29
31
  }
@@ -1,18 +1,22 @@
1
1
  /**
2
- * Stream Wiring - Publish flow events to UI clients via StreamAdapter
2
+ * Stream Wiring - Bridge event bus to StreamAdapter for real-time UI updates
3
3
  *
4
- * Subscribes to the local event bus (single source of truth) and publishes
5
- * persisted events to the StreamAdapter for WebSocket/SSE clients.
4
+ * Architecture:
5
+ * 1. Event Bus - Single source of truth for all events (in-memory)
6
+ * 2. StreamWiring - Bridges persisted events to stream topics (this file)
7
+ * 3. StreamAdapter - Pub/sub for real-time distribution (Redis/Memory)
8
+ * 4. WebSocket handlers - Subscribe to topics and send to clients
6
9
  *
7
- * Simple and focused:
8
- * - Listens to event bus for persisted events (with id/ts)
9
- * - Publishes to client channel: client:flow:{runId}
10
- * - UI gets real-time updates
10
+ * Flow:
11
+ * Event Bus StreamWiring StreamAdapter WebSocket UI
11
12
  *
12
- * Benefits:
13
- * - Single source of truth (event bus)
14
- * - Easy to debug (no republishing)
15
- * - Clear separation of concerns
13
+ * Stream Topics (defined in useStreamTopics):
14
+ * - stream:flow:events:{runId} - Flow events for specific run
15
+ * - stream:flow:stats - Flow statistics updates
16
+ * - stream:trigger:events:{triggerName} - Trigger events
17
+ * - stream:trigger:stats - Trigger statistics updates
18
+ *
19
+ * Only publishes persisted events (with id/ts) to avoid duplicates
16
20
  */
17
21
  export interface StreamWiringOptions {
18
22
  /**
@@ -13,14 +13,14 @@ export function createStreamWiring(opts = {}) {
13
13
  if (!enabled) return;
14
14
  const logger = useNventLogger("stream-wiring");
15
15
  const stream = useStreamAdapter();
16
- const { getClientFlowTopic } = useStreamTopics();
16
+ const { StreamTopics } = useStreamTopics();
17
17
  logger.info("Starting stream wiring for UI clients");
18
- const handleClientMessage = async (e) => {
18
+ const handleFlowEvent = async (e) => {
19
19
  if (!e.id || !e.ts) return;
20
20
  const runId = e.runId;
21
21
  if (!runId) return;
22
22
  try {
23
- const topic = getClientFlowTopic(runId);
23
+ const topic = StreamTopics.flowEvents(runId);
24
24
  await stream.publish(topic, {
25
25
  type: "flow.event",
26
26
  data: {
@@ -39,14 +39,46 @@ export function createStreamWiring(opts = {}) {
39
39
  },
40
40
  timestamp: Date.now()
41
41
  });
42
- logger.debug("Published to UI clients", { type: e.type, runId });
42
+ logger.debug("Published flow event to stream", { type: e.type, runId });
43
43
  } catch (err) {
44
- logger.error("Failed to publish to UI clients", {
44
+ logger.error("Failed to publish flow event to stream", {
45
45
  error: err?.message
46
46
  });
47
47
  }
48
48
  };
49
- const clientEventTypes = [
49
+ const handleTriggerEvent = async (e) => {
50
+ if (!e.id || !e.ts) return;
51
+ const triggerName = e.triggerName;
52
+ if (!triggerName) return;
53
+ try {
54
+ const topic = StreamTopics.triggerEvents(triggerName);
55
+ await stream.publish(topic, {
56
+ type: "trigger.event",
57
+ data: {
58
+ event: {
59
+ id: e.id,
60
+ ts: e.ts,
61
+ type: e.type,
62
+ triggerName,
63
+ data: e.data
64
+ }
65
+ },
66
+ timestamp: Date.now()
67
+ });
68
+ logger.debug("Published trigger event to stream", {
69
+ triggerName,
70
+ type: e.type,
71
+ id: e.id
72
+ });
73
+ } catch (err) {
74
+ logger.error("Failed to publish trigger event to stream", {
75
+ triggerName,
76
+ type: e.type,
77
+ error: err?.message
78
+ });
79
+ }
80
+ };
81
+ const flowEventTypes = [
50
82
  "flow.start",
51
83
  "flow.completed",
52
84
  "flow.failed",
@@ -55,14 +87,59 @@ export function createStreamWiring(opts = {}) {
55
87
  "step.completed",
56
88
  "step.failed",
57
89
  "step.retry",
90
+ "await.registered",
91
+ "await.resolved",
92
+ "await.timeout",
58
93
  "log",
59
- "emit",
60
- "state"
94
+ "emit"
61
95
  ];
62
- for (const type of clientEventTypes) {
63
- unsubs.push(bus.onType(type, handleClientMessage));
96
+ const triggerEventTypes = [
97
+ "trigger.registered",
98
+ "trigger.updated",
99
+ "trigger.deleted",
100
+ "trigger.fired",
101
+ "subscription.added",
102
+ "subscription.removed"
103
+ ];
104
+ const handleFlowStatsUpdate = async (e) => {
105
+ try {
106
+ const topic = StreamTopics.flowStats();
107
+ await stream.publish(topic, {
108
+ id: e.flowName,
109
+ metadata: e.metadata
110
+ });
111
+ logger.debug("Published flow stats to stream", { flowName: e.flowName });
112
+ } catch (err) {
113
+ logger.error("Failed to publish flow stats to stream", {
114
+ flowName: e.flowName,
115
+ error: err?.message
116
+ });
117
+ }
118
+ };
119
+ const handleTriggerStatsUpdate = async (e) => {
120
+ try {
121
+ const topic = StreamTopics.triggerStats();
122
+ await stream.publish(topic, {
123
+ id: e.triggerName,
124
+ metadata: e.metadata
125
+ });
126
+ logger.debug("Published trigger stats to stream", { triggerName: e.triggerName });
127
+ } catch (err) {
128
+ logger.error("Failed to publish trigger stats to stream", {
129
+ triggerName: e.triggerName,
130
+ error: err?.message
131
+ });
132
+ }
133
+ };
134
+ for (const type of flowEventTypes) {
135
+ unsubs.push(bus.onType(type, handleFlowEvent));
136
+ }
137
+ for (const type of triggerEventTypes) {
138
+ unsubs.push(bus.onType(type, handleTriggerEvent));
64
139
  }
65
- logger.info("Stream wiring started - listening for persisted events");
140
+ unsubs.push(bus.onType("flow.stats.updated", handleFlowStatsUpdate));
141
+ unsubs.push(bus.onType("trigger.stats.updated", handleTriggerStatsUpdate));
142
+ logger.info("Stream wiring started - listening for persisted flow and trigger events");
66
143
  }
67
144
  function stop() {
68
145
  const logger = useNventLogger("stream-wiring");
@@ -0,0 +1,21 @@
1
+ import type { TriggerFiredEvent } from '../types.js';
2
+ /**
3
+ * Create trigger event wiring
4
+ * Listens to trigger.fired events and starts subscribed flows
5
+ * Handles await.registered and await.resolved events
6
+ */
7
+ export declare function createTriggerWiring(): {
8
+ start: () => Promise<void>;
9
+ stop: () => void;
10
+ };
11
+ /**
12
+ * Handle trigger.fired event
13
+ * Starts all subscribed flows (auto mode) or queues them (manual mode)
14
+ * Returns list of flows that were started for stream metadata
15
+ */
16
+ export declare function handleTriggerFired(event: TriggerFiredEvent): Promise<string[]>;
17
+ /**
18
+ * Start a flow from a trigger event
19
+ * Enqueues the entry step and publishes flow.start event
20
+ */
21
+ export declare function startFlowFromTrigger(flowName: string, triggerName: string, triggerData: any): Promise<void>;
@@ -0,0 +1,412 @@
1
+ import { getEventBus } from "../eventBus.js";
2
+ import { useTrigger, useNventLogger, useStoreAdapter, useQueueAdapter, $useAnalyzedFlows, $useFunctionRegistry, useStreamTopics } from "#imports";
3
+ import { getTriggerRuntime } from "../utils/triggerRuntime.js";
4
+ import { scheduleTrigger, unscheduleTrigger } from "../utils/scheduleTrigger.js";
5
+ export function createTriggerWiring() {
6
+ const unsubs = [];
7
+ let wired = false;
8
+ async function start() {
9
+ if (wired) return;
10
+ wired = true;
11
+ const logger = useNventLogger("trigger-wiring");
12
+ const eventBus = getEventBus();
13
+ const trigger = useTrigger();
14
+ const store = useStoreAdapter();
15
+ const { StoreSubjects } = useStreamTopics();
16
+ const runtime = getTriggerRuntime(store, logger);
17
+ await trigger.initialize();
18
+ logger.info("Setting up trigger event wiring");
19
+ const handlePersistence = async (e) => {
20
+ try {
21
+ if (e.id && e.ts) {
22
+ return;
23
+ }
24
+ const triggerName = e.triggerName || e.data?.triggerName;
25
+ if (!triggerName) {
26
+ return;
27
+ }
28
+ const streamName = StoreSubjects.triggerStream(triggerName);
29
+ if (!e.type) {
30
+ logger.error("Event missing type field", { event: e });
31
+ return;
32
+ }
33
+ const eventData = {
34
+ type: e.type,
35
+ triggerName,
36
+ data: e.data
37
+ };
38
+ const persistedEvent = await store.stream.append(streamName, eventData);
39
+ await eventBus.publish(persistedEvent);
40
+ logger.debug("Stored trigger event", {
41
+ type: e.type,
42
+ triggerName,
43
+ id: persistedEvent.id
44
+ });
45
+ } catch (err) {
46
+ logger.error("ERROR persisting trigger event", {
47
+ type: e.type,
48
+ triggerName: e.triggerName,
49
+ error: err?.message
50
+ });
51
+ }
52
+ };
53
+ const handleOrchestration = async (e) => {
54
+ try {
55
+ if (e.id && e.ts) {
56
+ return;
57
+ }
58
+ const triggerName = e.triggerName || e.data?.triggerName;
59
+ if (!triggerName) {
60
+ logger.debug("Orchestration skipped - no triggerName", { type: e.type });
61
+ return;
62
+ }
63
+ logger.debug("Processing trigger orchestration", { type: e.type, triggerName });
64
+ const indexKey = StoreSubjects.triggerIndex();
65
+ const now = (/* @__PURE__ */ new Date()).toISOString();
66
+ const nowTimestamp = Date.now();
67
+ if (e.type === "trigger.registered") {
68
+ const data = e.data;
69
+ if (store.index.add) {
70
+ await store.index.add(indexKey, triggerName, nowTimestamp, {
71
+ "name": data.name,
72
+ "type": data.type,
73
+ "scope": data.scope,
74
+ "status": "active",
75
+ "displayName": data.displayName,
76
+ "description": data.description,
77
+ "source": data.source || "programmatic",
78
+ "registeredAt": now,
79
+ "registeredBy": "runtime",
80
+ "lastActivityAt": now,
81
+ "stats.totalFires": 0,
82
+ "stats.totalFlowsStarted": 0,
83
+ "stats.activeSubscribers": 0,
84
+ "webhook": data.webhook,
85
+ "schedule": data.schedule,
86
+ "config": data.config,
87
+ "version": 1
88
+ });
89
+ }
90
+ const entry = {
91
+ name: data.name,
92
+ type: data.type,
93
+ scope: data.scope,
94
+ status: "active",
95
+ displayName: data.displayName,
96
+ description: data.description,
97
+ source: data.source || "programmatic",
98
+ registeredAt: now,
99
+ registeredBy: "runtime",
100
+ lastActivityAt: now,
101
+ subscriptions: {},
102
+ stats: { totalFires: 0, totalFlowsStarted: 0, activeSubscribers: 0 },
103
+ webhook: data.webhook,
104
+ schedule: data.schedule,
105
+ config: data.config,
106
+ version: 1
107
+ };
108
+ runtime.addTrigger(triggerName, entry);
109
+ logger.info("Registered trigger in index", { triggerName });
110
+ if (data.type === "schedule" && data.schedule) {
111
+ await scheduleTrigger(triggerName, data.schedule, data.status || "active");
112
+ }
113
+ }
114
+ if (e.type === "trigger.updated") {
115
+ const data = e.data;
116
+ if (store.index.updateWithRetry) {
117
+ await store.index.updateWithRetry(indexKey, triggerName, {
118
+ type: data.type,
119
+ scope: data.scope,
120
+ status: data.status,
121
+ displayName: data.displayName,
122
+ description: data.description,
123
+ webhook: data.webhook,
124
+ schedule: data.schedule,
125
+ config: data.config,
126
+ lastActivityAt: now
127
+ });
128
+ }
129
+ const existing = runtime.getTrigger(triggerName);
130
+ if (existing) {
131
+ const updated = {
132
+ ...existing,
133
+ status: data.status !== void 0 ? data.status : existing.status,
134
+ displayName: data.displayName !== void 0 ? data.displayName : existing.displayName,
135
+ description: data.description !== void 0 ? data.description : existing.description,
136
+ webhook: data.webhook !== void 0 ? data.webhook : existing.webhook,
137
+ schedule: data.schedule !== void 0 ? data.schedule : existing.schedule,
138
+ config: data.config !== void 0 ? data.config : existing.config,
139
+ lastActivityAt: now
140
+ };
141
+ runtime.addTrigger(triggerName, updated);
142
+ if (updated.type === "schedule" && updated.schedule) {
143
+ await scheduleTrigger(triggerName, updated.schedule, updated.status);
144
+ }
145
+ }
146
+ logger.info("Updated trigger in index and runtime", { triggerName, status: data.status });
147
+ }
148
+ if (e.type === "subscription.added") {
149
+ const data = e.data;
150
+ const { flow, mode } = data;
151
+ const existingSub = runtime.getSubscription(triggerName, flow);
152
+ if (store.index.updateWithRetry) {
153
+ await store.index.updateWithRetry(indexKey, triggerName, {
154
+ subscriptions: {
155
+ [flow]: {
156
+ mode,
157
+ subscribedAt: existingSub ? existingSub.registeredAt || now : now
158
+ }
159
+ },
160
+ lastActivityAt: now
161
+ });
162
+ if (!existingSub && store.index.increment) {
163
+ await store.index.increment(indexKey, triggerName, "stats.activeSubscribers", 1);
164
+ }
165
+ }
166
+ const subscription = {
167
+ triggerName,
168
+ flowName: flow,
169
+ mode,
170
+ source: "programmatic",
171
+ registeredAt: existingSub?.registeredAt || now
172
+ };
173
+ runtime.addSubscription(triggerName, flow, subscription);
174
+ logger.info(`Subscription ${existingSub ? "updated" : "added"}`, { triggerName, flow, mode });
175
+ }
176
+ if (e.type === "subscription.removed") {
177
+ const data = e.data;
178
+ const { flow } = data;
179
+ if (store.index.updateWithRetry) {
180
+ await store.index.updateWithRetry(indexKey, triggerName, {
181
+ subscriptions: {
182
+ [flow]: null
183
+ // null removes the field
184
+ },
185
+ lastActivityAt: now
186
+ });
187
+ if (store.index.increment) {
188
+ await store.index.increment(indexKey, triggerName, "stats.activeSubscribers", -1);
189
+ }
190
+ }
191
+ runtime.removeSubscription(triggerName, flow);
192
+ logger.info("Subscription removed", { triggerName, flow });
193
+ }
194
+ if (e.type === "trigger.fired") {
195
+ const flowsStarted = await handleTriggerFired(e);
196
+ if (flowsStarted.length > 0 && store.index.increment) {
197
+ await store.index.increment(indexKey, triggerName, "stats.totalFlowsStarted", flowsStarted.length);
198
+ }
199
+ logger.debug("Trigger fired and processed", { triggerName, flowsStarted: flowsStarted.length });
200
+ }
201
+ if (e.type === "trigger.deleted") {
202
+ const triggerEntry = runtime.getTrigger(triggerName);
203
+ const wasScheduleTrigger = triggerEntry?.type === "schedule";
204
+ if (store.index.delete) {
205
+ await store.index.delete(indexKey, triggerName);
206
+ }
207
+ const triggerStreamKey = StoreSubjects.triggerStream(triggerName);
208
+ if (store.stream.delete) {
209
+ await store.stream.delete(triggerStreamKey);
210
+ }
211
+ runtime.removeTrigger(triggerName);
212
+ if (wasScheduleTrigger) {
213
+ await unscheduleTrigger(triggerName);
214
+ }
215
+ logger.info("Trigger deleted completely", { triggerName });
216
+ }
217
+ } catch (err) {
218
+ logger.error("ERROR in trigger orchestration", {
219
+ type: e.type,
220
+ triggerName: e.triggerName,
221
+ error: err?.message,
222
+ stack: err?.stack
223
+ });
224
+ }
225
+ };
226
+ const handleTriggerStats = async (e) => {
227
+ try {
228
+ if (!e.id || !e.ts) {
229
+ return;
230
+ }
231
+ const triggerName = e.triggerName || e.data?.triggerName;
232
+ if (!triggerName) return;
233
+ const indexKey = StoreSubjects.triggerIndex();
234
+ const now = (/* @__PURE__ */ new Date()).toISOString();
235
+ if (e.type === "trigger.fired") {
236
+ if (store.index.updateWithRetry) {
237
+ await store.index.updateWithRetry(indexKey, triggerName, {
238
+ stats: {
239
+ lastFiredAt: now
240
+ },
241
+ lastActivityAt: now
242
+ });
243
+ }
244
+ if (store.index.increment) {
245
+ await store.index.increment(indexKey, triggerName, "stats.totalFires", 1);
246
+ }
247
+ logger.debug("Updated trigger stats for fire", { triggerName });
248
+ }
249
+ try {
250
+ if (store.index.get) {
251
+ const indexEntry = await store.index.get(indexKey, triggerName);
252
+ if (indexEntry) {
253
+ await eventBus.publish({
254
+ type: "trigger.stats.updated",
255
+ triggerName,
256
+ id: indexEntry.id,
257
+ metadata: indexEntry.metadata,
258
+ ts: Date.now()
259
+ });
260
+ logger.debug("Published trigger stats update event to bus", { triggerName });
261
+ }
262
+ }
263
+ } catch (err) {
264
+ logger.warn("Failed to publish trigger stats update event", {
265
+ triggerName,
266
+ error: err?.message
267
+ });
268
+ }
269
+ } catch (err) {
270
+ logger.warn("Failed to update trigger stats", {
271
+ type: e.type,
272
+ triggerName: e.triggerName,
273
+ error: err?.message
274
+ });
275
+ }
276
+ };
277
+ const eventTypes = [
278
+ "trigger.registered",
279
+ "trigger.updated",
280
+ "trigger.deleted",
281
+ "trigger.fired",
282
+ "subscription.added",
283
+ "subscription.removed"
284
+ ];
285
+ const triggerStatsEventTypes = ["trigger.fired"];
286
+ for (const type of eventTypes) {
287
+ unsubs.push(eventBus.onType(type, handlePersistence));
288
+ }
289
+ for (const type of eventTypes) {
290
+ unsubs.push(eventBus.onType(type, handleOrchestration));
291
+ }
292
+ for (const type of triggerStatsEventTypes) {
293
+ unsubs.push(eventBus.onType(type, handleTriggerStats));
294
+ }
295
+ logger.info("Trigger event wiring setup complete (persistence + orchestration + stats)");
296
+ }
297
+ function stop() {
298
+ const logger = useNventLogger("trigger-wiring");
299
+ for (const u of unsubs.splice(0)) {
300
+ try {
301
+ u();
302
+ } catch {
303
+ }
304
+ }
305
+ wired = false;
306
+ logger.debug("Trigger wiring stopped");
307
+ }
308
+ return { start, stop };
309
+ }
310
+ export async function handleTriggerFired(event) {
311
+ const logger = useNventLogger("trigger-wiring");
312
+ const trigger = useTrigger();
313
+ const { triggerName, data } = event;
314
+ logger.debug("Trigger fired", { trigger: triggerName });
315
+ const subscriptions = trigger.getAllSubscriptions().filter((sub) => sub.triggerName === triggerName);
316
+ if (subscriptions.length === 0) {
317
+ logger.warn(`No flows subscribed to trigger: ${triggerName}`);
318
+ return [];
319
+ }
320
+ const flowsStarted = [];
321
+ const store = useStoreAdapter();
322
+ const loggerForRuntime = useNventLogger("trigger-runtime");
323
+ const runtime = getTriggerRuntime(store, loggerForRuntime);
324
+ const resolvedData = await runtime.resolvePayload(data);
325
+ for (const subscription of subscriptions) {
326
+ try {
327
+ if (subscription.mode === "manual") {
328
+ logger.info(
329
+ `Trigger '${triggerName}' fired for flow '${subscription.flowName}' (manual mode - awaiting manual start)`
330
+ );
331
+ continue;
332
+ }
333
+ await startFlowFromTrigger(subscription.flowName, triggerName, resolvedData);
334
+ flowsStarted.push(subscription.flowName);
335
+ } catch (error) {
336
+ logger.error("Error starting flow from trigger", {
337
+ flow: subscription.flowName,
338
+ trigger: triggerName,
339
+ error: error instanceof Error ? error.message : String(error)
340
+ });
341
+ }
342
+ }
343
+ return flowsStarted;
344
+ }
345
+ export async function startFlowFromTrigger(flowName, triggerName, triggerData) {
346
+ const logger = useNventLogger("trigger-wiring");
347
+ const eventBus = getEventBus();
348
+ const queue = useQueueAdapter();
349
+ const registry = $useFunctionRegistry();
350
+ const analyzedFlows = $useAnalyzedFlows();
351
+ const store = useStoreAdapter();
352
+ const triggerRuntime = getTriggerRuntime(store, logger);
353
+ const flowDef = analyzedFlows.find((f) => f.id === flowName);
354
+ if (!flowDef || !flowDef.entry) {
355
+ logger.error(`Flow '${flowName}' not found or has no entry point`);
356
+ return;
357
+ }
358
+ const flowRegistry = (registry?.flows || {})[flowName];
359
+ if (!flowRegistry?.entry) {
360
+ logger.error(`Flow '${flowName}' has no entry in registry`);
361
+ return;
362
+ }
363
+ const triggerDef = triggerRuntime.getTrigger(triggerName);
364
+ const triggerType = triggerDef?.type || "manual";
365
+ const runId = `${flowName}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
366
+ logger.info(`Starting flow '${flowName}' from trigger '${triggerName}'`, { runId });
367
+ const queueName = flowRegistry.entry.queue;
368
+ const stepName = flowRegistry.entry.step;
369
+ const payload = {
370
+ flowId: runId,
371
+ flowName,
372
+ trigger: {
373
+ name: triggerName,
374
+ type: triggerType,
375
+ data: triggerData
376
+ },
377
+ ...triggerData
378
+ };
379
+ const entryWorker = registry?.workers?.find(
380
+ (w) => w?.flow?.step === stepName && w?.queue?.name === queueName
381
+ );
382
+ const defaultOpts = entryWorker?.queue?.defaultJobOptions || {};
383
+ const jobId = `${runId}__${stepName}`;
384
+ const opts = { ...defaultOpts, jobId };
385
+ try {
386
+ await queue.enqueue(queueName, {
387
+ name: stepName,
388
+ data: payload,
389
+ opts
390
+ });
391
+ logger.info(`Enqueued entry step '${stepName}' to queue '${queueName}'`, { runId });
392
+ await eventBus.publish({
393
+ type: "flow.start",
394
+ flowName,
395
+ runId,
396
+ data: {
397
+ input: triggerData,
398
+ trigger: {
399
+ name: triggerName,
400
+ type: triggerType,
401
+ data: triggerData
402
+ }
403
+ }
404
+ });
405
+ } catch (error) {
406
+ logger.error("Failed to start flow from trigger", {
407
+ flowName,
408
+ trigger: triggerName,
409
+ error: error instanceof Error ? error.message : String(error)
410
+ });
411
+ }
412
+ }
@@ -1,4 +1,4 @@
1
- import { defineNitroPlugin, useRuntimeConfig, useNventLogger, setAdapters } from "#imports";
1
+ import { defineNitroPlugin, useRuntimeConfig, useNventLogger, setAdapters, initializeScheduler, shutdownScheduler } from "#imports";
2
2
  import { createAdapters, shutdownAdapters } from "../../adapters/factory.js";
3
3
  import { useAdapterRegistry } from "../../adapters/registry.js";
4
4
  import { createWiringRegistry } from "../../events/wiring/registry.js";
@@ -34,7 +34,8 @@ export default defineNitroPlugin(async (nitroApp) => {
34
34
  streamAdapter: config.stream.adapter,
35
35
  storeAdapter: config.store.adapter
36
36
  });
37
- await nitroApp.hooks.callHook("nvent:adapters:ready");
37
+ await initializeScheduler(adapters.store);
38
+ logger.info("Scheduler initialized");
38
39
  const wiring = createWiringRegistry({
39
40
  streamWiring: {
40
41
  enabled: true
@@ -45,15 +46,18 @@ export default defineNitroPlugin(async (nitroApp) => {
45
46
  // Can be overridden here if needed
46
47
  }
47
48
  });
48
- wiring.start();
49
+ await wiring.start();
49
50
  logger.info("Flow wiring started");
51
+ await nitroApp.hooks.callHook("nvent:adapters:ready");
50
52
  return {
51
53
  hooks: {
52
54
  close: async () => {
53
55
  logger.info("Shutting down");
54
56
  try {
55
- wiring.stop();
57
+ await wiring.stop();
56
58
  logger.info("Flow wiring stopped");
59
+ await shutdownScheduler();
60
+ logger.info("Scheduler shut down");
57
61
  await shutdownAdapters(adapters);
58
62
  logger.info("Adapters shut down successfully");
59
63
  } catch (error) {